there is a json file that returns 'true' or 'false' (Bool).
How can I parse it.
Json file doesn't have key. It returns only
true
or
false
JSONDecoder is overkill for this purpose.
The Data representation of JSON true is
let data = Data([0x74, 0x72, 0x75, 0x65])
To check it simply create a String and compare with "true"
let isRegistered = String(data: data, encoding: .utf8) == "true"
or compare the raw Data directly
let isRegistered = data == Data([0x74, 0x72, 0x75, 0x65])
While I would almost certainly do this the way vadian suggests, JSONDecoder can definitely decode a top-level Bool:
let data = Data("true".utf8)
let value = try JSONDecoder().decode(Bool.self, from: data)
There's no need for a container. The (very slight) advantage of decoding this way is that it's much easier to make it tolerant of small differences like extra whitespace (for example a trailing newline). It's also easier to detect invalid data (for example, an empty result or an error) and distinguish it from false.
You need let container = try decoder.singleValueContainer()
struct RegisteredModel: Codable{
var registered:Bool
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
registered = try container.decode(Bool.self)
}
}
let json = "false"
let data = json.data(using: .utf8)!
do {
let result = try JSONDecoder().decode(RegisteredModel.self, from: data)
print(result)
}
catch {
print(error)
}
Related
To understand the problem I need to provide some background to what I am trying to achieve.
I am trying to create a JWT with this library that will be signed with a private key.
My problem is that the dictionary that I use to create the JSON is unordered, which therefore results in an unordered JSON string.
The code below prints the JSON string in any order.
let dictionary = ["aKey": "aValue", "anotherKey": "anotherValue"]
if let theJSONData = try? JSONSerialization.data(
withJSONObject: dictionary,
options: .prettyPrinted
),
let theJSONText = String(data: theJSONData,
encoding: String.Encoding.ascii) {
print("JSON string = \n\(theJSONText)")
}
let privateKey = theJSONText.data(using: .utf8)
let jwtSigner = JWTSigner.hs256(privateKey: privateKey)
let signedJWT = try myJWT.sign(using: jwtSigner) // This produces a JWT with an invalid signature
The result of this is that my JWT produces an invalid signature. How do I produce a JSON string that has order?
JSONEncoder encodes a dictionary always in the same order (which however might not be the given order of the dictionary). But maybe it fulfills your needs.
if let theJSONData = try? JSONEncoder().encode(dictionary) {
let theJSONText = String(data: theJSONData, encoding: .utf8)!
print("JSON string = \n\(theJSONText)")
}
And there is also the sortedKeys option of OutputFormatting
And – once again – never prettyPrint anything which is going to be sent to a server.
I have tried pretty much all the potential solutions on stackoverflow and so far no luck,
This is my json response:
[
"{\"id\":5,\"request_id\":\"rqst5c17fc752d44f1.15452158\",\"business_name\":\"611 Solutions\",\"business_email\":\"611thesolutions#gmail.com\",\"title\":\"123ABC - TESTING\",\"details\":\"Package is fragile, please haul with care\",\"load_description\":\"Royal Timber\",\"amount_offered\":\"2500\",\"pickup_address\":\"123 Colliumeal Dr, Fort Wayne, Indiana\",\"dropoff_address\":\"647 Airportway, Chicago, Illinois\",\"timestamp\":\"2018-12-17 19:43:49\"}"
]
Notice there are backslashes within the key and values of the json and my parsing is failing, this is how I am parse the json:
Alamofire.request(JOB_REQUEST_BASE_URL, method: .post, parameters: parameter, encoding: URLEncoding(), headers: nil).responseArray { (response: DataResponse<[JobResponseDataObject]>) in
log.debug("Fetching Job Requests...")
switch response.result {
case .success(let responseArray) :
log.debug(response.debugDescription)
log.debug("Sucessfully fetch job requests")
log.debug("Job request counts: \(responseArray.count)")
completionHandler(JobRequest.fetchJobRequest.Response(jobResponses: responseArray), nil)
case .failure(let error) :
log.debug("Fetching error: JobRequest")
log.debug(error.localizedDescription)
completionHandler(nil, .FailedToFetchEmptyJobRequests)
}
}
I have also tried fetching the pure string using .responseString and doing let json = response.result.value?.replacingOccurrences(of: "\\", with: "") and mapping it like so let jobs = Mapper<JobResponseDataObject>().map(JSONString: json!) so far no luck too. Please help
Thanks
You can try
if let str = responseArray.first as? String , let data = str.data(using:.utf8) {
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let res = try decoder.decode(Root.self,from:data)
}
catch {
print(error)
}
}
struct Root: Codable {
let id: Int
let requestId, businessName, businessEmail, title: String
let details, loadDescription, amountOffered, pickupAddress: String
let dropoffAddress, timestamp: String
}
You don't need to remove backslashes - it's just serilized one more time, that means it needs to deserialize back.
Look at: Why json response includes backward slashes in web api response
Just make a Data object from the string item:
let data = stringItem.data(using: .utf8)
then decode normally using JSONDecoder.
I am developing an iOS App that fetches Trivia Questions from Open Trivia Database (API)
After reading the docs and played around with it I think that the best solution is to use base64 encoding (since it seems to be supported in Swift). I have successfully fetched the data and parsed it into structs using a JSONParser. The problem that I have to solve is how to convert the values from base64 to UTF8. (The keys are read correctly, and therefore it maps to my structs)
My first idea was to use decoder.dataDecodingStrategy = .base64, but that does not seem to have any effect at all. And I am not really sure why.
Is that the right way to do it, or should I decode it myself afterwards when the strings are read in to structs?
In short, the result of the Parsing is a struct containing a responseCode as an Int and array containing structs representing the questions with the strings that I want to convert to UTF8 as members
My code for parsing looks like this:
let urlPath = "https://opentdb.com/api.php?amount=10&encode=base64"
let apiURL = URL(string: urlPath)!
URLSession.shared.dataTask(with: apiURL) { (data, response, error) in
guard let data = data else {return}
do{
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = .base64
let questionData = try decoder.decode(Response.self, from: data)
print(questionData)
}catch let err{
print("Error", err)
}
}.resume()
Base64 encoding is used for properties you declared as Data, not as Strings, like so:
struct Response: Codable {
let someBaseEncodedString: Data
var someString: String? {
get {
return String(data: someBaseEncodedString, encoding: .utf8)
}
}
}
So, for the example you are giving, all the properties that are returned as a base64 encoded string should have the Data type in your struct, and then after that decoded as strings.
As suggested by other answers, you can decode Data or Base-64 String after JSONSerialization or JSONDecoder decoded the API results.
But if you prefer to write decoding initializer, you can make it as follows:
This may not be much different from your own Response, I guess.
struct Response: Codable {
var responseCode: Int
var results: [Result]
enum CodingKeys: String, CodingKey {
case responseCode = "response_code"
case results
}
}
To prepare to write a decoding initializer for Response, I would like to use some extensions:
extension KeyedDecodingContainer {
func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> String {
guard let string = try self.decode(String.self, forKey: key).decodeBase64(encoding: encoding) else {
throw DecodingError.dataCorruptedError(forKey: key, in: self,
debugDescription: "Not a valid Base-64 representing UTF-8")
}
return string
}
func decodeBase64(forKey key: Key, encoding: String.Encoding) throws -> [String] {
var arrContainer = try self.nestedUnkeyedContainer(forKey: key)
var strings: [String] = []
while !arrContainer.isAtEnd {
guard let string = try arrContainer.decode(String.self).decodeBase64(encoding: encoding) else {
throw DecodingError.dataCorruptedError(forKey: key, in: self,
debugDescription: "Not a valid Base-64 representing UTF-8")
}
strings.append(string)
}
return strings
}
}
Using these extensions above, you can define the Result type as follows:
extension Response {
struct Result: Codable {
var category: String
var type: String
var difficulty: String
var question: String
var correctAnswer: String
var incorrectAnswers: [String]
enum CodingKeys: String, CodingKey {
case category
case type
case difficulty
case question
case correctAnswer = "correct_answer"
case incorrectAnswers = "incorrect_answers"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.category = try container.decodeBase64(forKey: .category, encoding: .utf8)
self.type = try container.decodeBase64(forKey: .type, encoding: .utf8)
self.difficulty = try container.decodeBase64(forKey: .difficulty, encoding: .utf8)
self.question = try container.decodeBase64(forKey: .question, encoding: .utf8)
self.correctAnswer = try container.decodeBase64(forKey: .correctAnswer, encoding: .utf8)
self.incorrectAnswers = try container.decodeBase64(forKey: .incorrectAnswers, encoding: .utf8)
}
}
}
(You have not mentioned if your Response (or other name?) is defined as a nested type or not, but I think you can rename or modify it yourself.)
With all things above, you can simply decode the API response as:
do {
let decoder = JSONDecoder()
let questionData = try decoder.decode(Response.self, from: data)
print(questionData)
} catch {
print("Error", error)
}
By the way, you say I think that the best solution is to use base64 encoding (since it seems to be supported in Swift), but is that really true?
Base-64 to Data is supported in JSONDecoder, but it is not what you expect. So, using another encoding can be a better choice.
But, anyway, JSON string can represent all unicode characters using only ASCII with \uXXXX or \uHHHH\uLLLL. So, I do not understand why the API designers do not provide an option Standard JSON Encoding. If you can contact to them, please tell them to provide the option, that may simplify many client side codes.
This is too simple but I am lost. I am still new to swift really.
I need to parse the downloaded json ( localized file in the Xcode project ) and populate the data to a CollectionView.
enum Response{
case success(Data)
case error(Error)
}
// struct follows the json
struct InformationFromJson: Decodable {
let id: Int
let name: String
}
class MYJSON {
public func downloadMYJSON(_ completion: #escaping (Response) -> ()) {
guard let bundle = Bundle(identifier: MYJSON.bundleId), let path = bundle.path(forResource: "data", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else {
completion(Response.error(NSError(domain: MYJSON.bundleId, code: MYJSON.bundleErrorCode, userInfo: [NSLocalizedDescriptionKey : MYJSON.bundleError])))
return
}
completion(Response.success(data))
}
}
So, without totally changing the function call, how do I parse the json? It's downloaded so far from the function, but I don't see how to even add a print statement to test, without getting errors because of the guard statement , the way it is.
I need to simple populate a cellForRowAt:
I never saw nested guard like this, so it got me. I am used to seeing the let statements separated so you can put print statements to at least see if things are getting downloaded or parsed.
You can decode your json by passing data, whatever you get from
let data = try? Data(contentsOf: URL(fileURLWithPath: path))
guard let decoded = try? JSONDecoder().decode(InformationFromJson.self, from: data) else {
return
}
I have a JSON array created using this call:
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [Any] else {
print("This is not JSON!!!")
return
}
I am trying to get elements from the JSON objects in the array to display them using the following code:
struct sWidget: Codable{
var createdBy: String
var createdDate: Date
var status: String
var widgetNumber: String
var updatedBy: String
var updatedDate: Date
}
do {
let decoder = JSONDecoder()
for (index, value) in json.enumerated() {
let currentWidget = try decoder.decode(sWidget.self, from: json[index] as! Data)
let currentNum = currentWidget.widgetNumber
//print(currentNum)
widgetNums.append(currentNum)
}
}
catch {
print("decoding error")
}
The code compiles but when I run it I get this error in the output:
Could not cast value of type '__NSDictionaryM' (0x1063c34f8) to
'NSData' (0x1063c1090). 2018-08-09 09:41:02.666713-0500
TruckMeterLogScanner[14259:1223764] Could not cast value of type
'__NSDictionaryM' (0x1063c34f8) to 'NSData' (0x1063c1090).
I am still investigating but any tips would be helpful.
Did you try that fetching objects like above mentioned? Because i see that you are using Codable. Fetching is very simple with that actually.
let yourObjectArray = JSONDecoder().decode([sWidget].self, data: json as! Data)
May be this line can be buggy but you can fetch them with one line.
Extending #Cemal BAYRI's answer:
JSONDecoder() throws, so make sure to either us try? or try (don't forget do-catch with try)
guard let data = content as? Data else {
return [sWidget]()
}
let jsonDecoder = JSONDecoder()
1. try?
let yourObjectArray = try? jsonDecoder.decode([sWidget].self, data: data)
2. try
do {
let yourObjectArray = try jsonDecoder.decode([sWidget].self, data: data)
} catch let error {
}
Note: You would need to take care of Data and Date formatting. Below is an example for Date:
jsonDecoder.dateDecodingStrategy = .iso8601
You can also check it out here