Parsing error with NSJSONSerialization - json

I am facing error while trying to parse JSON from a URL.
Following is the code:
override func viewDidLoad() {
super.viewDidLoad()
print("hello")
let url=NSURL(string:"http://jsonReports/sample_testkrtk252.json")
print("hello2")
if let allContactsData=NSData(contentsOfURL:url!)
{
print(allContactsData)
do{
let allContacts: AnyObject! = try NSJSONSerialization.JSONObjectWithData(allContactsData, options: NSJSONReadingOptions.MutableContainers)
print(allContacts)
if let json = allContacts as? Array<AnyObject> {
print(json)
for index in 0...json.count-1 {
let data12 : AnyObject? = json[index]
print(data12)
let collection = data12! as! Dictionary<String, AnyObject>
print(collection)
print(collection["data11"])
let data11 : AnyObject? = collection["data11"]
let cont : AnyObject? = collection["cont"]
data1.append(data11 as! String)
data2.append(cont as! String)
print(data1)
print(data2)
}
}
}
catch
{
print("error")
}
}
And after the successful run it gives the following output:
hello
hello2
<7b0d0a20 20202022 ... >
error
(lldb)
Also, it gives the error:
NSError Domain: "NSCocoaErrorDomain" - code: 3840 0x00007f8b62c9ef50 _userInfo __NSDictionaryI * 1 key/value pair 0x00007f8b62dcb3c0
It will be great if someone can look into this. Also, is there any better way to parse JSON in Swift?

your file .json is not a valid structure. Delete the first line:
"registration_id":APA91bEVsOgzkFFDCuEFn8PAS-FQqeaW6TRuz09CeKSnAJUSJmTvP8ubIsUkUe2zOzz8vk-FNqbNteOcD6m8m5nhrNWA96swZHYyX4nvY-mPCJTeBkEXTLuLwWCglbAUVCqJlwharLLJ,
Now try to run the code:
func run()->(Void){
//insert you file .jso into project. Or change the code
let filePath = NSBundle.mainBundle().pathForResource("sample_testkrtk252", ofType:"json")
let nsMutData:NSData = NSData(contentsOfFile:filePath!)!
do{
let allContacts:[String:AnyObject] = try NSJSONSerialization.JSONObjectWithData(nsMutData, options: NSJSONReadingOptions.MutableContainers) as! [String : AnyObject]
print(allContacts)
for temp in 0...allContacts.count-1 {
print(temp)
}
} catch
{
print("error")
}
}

Why not directly casting the AnyObject returned JSONObjectWithData to an array of NSDictionary? Like this:
let allContacts = try NSJSONSerialization.JSONObjectWithData(allContactsData, options: NSJSONReadingOptions.MutableContainers) as! [NSDictionary]
Are you sure your JSON is valid? Swift is very sensitive about that. Make sure all keys are quoted and you didn't forget any braces (especially the most outer ones).
Also for JSON parsing I would recommend SwiftyJSON which makes things a lot easier.

Thanks for your answers.
just editing the json worked for me.
yes, it is true that one needs to follow the format correctly, otherwise a simple double quote can disrupt the expected output. :)
"registration_id":"APA91bEVsOgzkFFDCuEFn8PAS-FQqeaW6TRuz09CeKSnAJUSJmTvP8ubIsUkUe2zOzz8vk-FNqbNteOcD6m8m5nhrNWA96swZHYyX4nvY-mPCJTeBkEXTLuLwWCglbAUVCqJlwharLLJ",
Thanks,
Poulami

Related

Swift MacOS getting bad json response from jsonSerialization

I am trying to convert a string to JSON in Swift.
Here's the string, which I am getting by pulling the innerHTML from a WKWebView.
{"list":{"pagination":{"count":3,"hasMoreItems":false,"totalItems":3,"skipCount":0,"maxItems":100},"entries":[{"entry":{"createdAt":"2020-06-16T21:00:32.714+0000","isFolder":false,"isFile":true,"createdByUser":{"id":"UserFName.userLName#xxxxxxxx.com","displayName":"UserFName userLName"},"modifiedAt":"2020-06-16T21:00:32.714+0000","modifiedByUser":{"id":"UserFName.userLName#xxxxxxxx.com","displayName":"UserFName userLName"},"naxxxxxxxxme":"00-invest-2020-06-16-17-00-32-716.txt","id":"028b4c82-09b8-4ee5-b4fa-9696a33b026d","nodeType":"log:fileNode","content":{"mimeType":"text/plain","mimeTypeName":"Plain Text","sizeInBytes":609373,"encoding":"UTF-8"},"parentId":"ba647bfc-a889-4d91-9211-4220cfe7d90a"}},{"entry":{"createdAt":"2020-06-16T21:01:12.828+0000","isFolder":false,"isFile":true,"createdByUser":{"id":"UserFName.userLName#xxxxxxxx.com","displayName":"UserFName userLName"},"modifiedAt":"2020-06-16T21:01:12.828+0000","modifiedByUser":{"id":"UserFName.userLName#xxxxxxxx.com","displayName":"UserFName userLName"},"name":"00-monetize-2020-06-16-17-01-12-830.txt","id":"d6412e3a-fea5-4d4d-a962-d91cde294bc9","nodeType":"log:fileNode","content":{"mimeType":"text/plain","mimeTypeName":"Plain Text","sizeInBytes":996653,"encoding":"UTF-8"},"parentId":"ba647bfc-a889-4d91-9211-4220cfe7d90a"}},{"entry":{"createdAt":"2020-06-16T18:33:49.344+0000","isFolder":true,"isFile":false,"createdByUser":{"id":"UserFName.userLName#xxxxxxxx.com","displayName":"UserFName userLName"},"modifiedAt":"2020-06-16T18:34:49.211+0000","modifiedByUser":{"id":"UserFName.userLName#xxxxxxxx.com","displayName":"UserFName userLName"},"name":"20200616","id":"d881db96-ddcb-44ae-99e1-ffe3ac0c2810","nodeType":"cm:folder","parentId":"2fcf4c49-be4c-4f2c-a90b-654ae092c63e"}}]}}
I've checked the string in JSON Lint and it says it is valid.
Here's what I am doing in my code to convert it:
let strJSONLiteral = """
\(strJSON)
"""
//convert string to json
let data = strJSONLiteral.data(using: .utf8)!
do {
if let myJSON = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? [Dictionary<String,Any>]
{
print(myJSON) // use the json here
} else {
print("bad json")
}
} catch let error as NSError {
print(error.localizedDescription)
}
The error is occurring in the JSONSerialization attempt. I'm getting nil for myJSON. Data check looked ok, has 1600+ bytes.
The top-level JSON is of type Dictionary while you're trying to decode an Array of Dictionary. To fix this
Replace:
if let myJSON = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? [Dictionary<String,Any>]
With:
if let myJSON = try JSONSerialization.jsonObject(with: data, options : .allowFragments) as? Dictionary<String,Any>
Or you can just use [String: Any].
Add-on: You should probably do a bit of research on Codable and try to use it for these scenarios.

Convert string preceeded with 0 to Json in Swift 4

I'm trying to encode an integer that starts with a 0 into JSON using swift 4.
I'm using a pretty standard JSONSerialization library, but for some reason, after converting the string to data using utf8, I cannot serialize it.
let code = "012345" // example code
let body = "{\"code\": " + code + "}"
let stringData = body.data(using: .utf8)!
let jsonArray = try? JSONSerialization.jsonObject(with: stringData, options : .allowFragments) [returns nil]
let data: Data? = try? JSONSerialization.data(withJSONObject: jsonArray as Any, options: .prettyPrinted)
Currently, the code breaks on the second to last line (starting with let jsonArray) and returns nil. Note that if I were to change code to "112345", there would be no error. Any help is appreciated, thanks!
Instead of manually creating string, use Dictionary and JSONSerialization to create data as below,
let code = "012345"
let body: [String: Any] = ["code": code]
do {
let stringData = try JSONSerialization.data(withJSONObject: body, options: .sortedKeys)
print(String.init(data: stringData, encoding: .utf8)!)
} catch {
print(error)
}
Output
{"code":"012345"}

Parsing json response with nested " in swift

I wanted to know the best way to parse json response of below type in Swift 4. Response is double encoded -
\"[{\\"value\\":\\"International University \\\\"MITSO\\\\"\\",\\"id\\":\\"a1v24000000uOrPAAU\\",\\"addlFields\\":[\\"Mi?narodny Universitet \\\\"MITSO\\\\"\\"]}]\"
Here is the data in NSData format -
(String) $R0 = "data: Optional(146 bytes) as NSData: <225b7b5c 2276616c 75655c22 3a5c2249 6e746572 6e617469 6f6e616c 20556e69 76657273 69747920 5c5c5c22 4d495453 4f5c5c5c 225c222c 5c226964 5c223a5c 22613176 32343030 30303030 754f7250 4141555c 222c5c22 6164646c 4669656c 64735c22 3a5b5c22 4d693f6e 61726f64 6e792055 6e697665 72736974 6574205c 5c5c224d 4954534f 5c5c5c22 5c225d7d 5d22>"
As you see value of the key "value" has a inner double quotes(").
JSONSerialization consider this as invalid Json.
Any help will be greatly appreciated.
The content of your data as String is as follows:
"[{\"value\":\"International University \\\"MITSO\\\"\",\"id\":\"a1v24000000uOrPAAU\",\"addlFields\":[\"Mi?narodny Universitet \\\"MITSO\\\"\"]}]"
Seeing the actual content without extra double-quotes and backslashes needed to show String as String-literal, it looks like some valid JSON is embedded in a String.
This may happen when the server side code double-encodes the data. You should better tell your server side engineer to fix the issue, but if it is difficult or would take long time, you can double-decode it.
Testing code:
import Foundation
let dataStr = "<225b7b5c 2276616c 75655c22 3a5c2249 6e746572 6e617469 6f6e616c 20556e69 76657273 69747920 5c5c5c22 4d495453 4f5c5c5c 225c222c 5c226964 5c223a5c 22613176 32343030 30303030 754f7250 4141555c 222c5c22 6164646c 4669656c 64735c22 3a5b5c22 4d693f6e 61726f64 6e792055 6e697665 72736974 6574205c 5c5c224d 4954534f 5c5c5c22 5c225d7d 5d22>".dropFirst().dropLast().replacingOccurrences(of: " ", with: "")
let byteArr = stride(from: 0, to: dataStr.count, by: 2).map{(index: Int)->UInt8 in
let start = dataStr.index(dataStr.startIndex, offsetBy: index)
let end = dataStr.index(start, offsetBy: 2)
return UInt8(dataStr[start..<end], radix: 16)!
}
let responseData = Data(bytes: byteArr)
print(responseData as NSData)
Check here, whether the print statement output is exactly the same as your sample response. (If you want to test the following code with your actual data than sample response, use just let responseData = result as! Data instead of above lines.)
So, you just need to use JSONSerialization twice:
block: do {
let firstDecoded = try JSONSerialization.jsonObject(with: responseData, options: .allowFragments) as! String
let firstDecodedData = firstDecoded.data(using: .utf8)!
let secondDecoded = try JSONSerialization.jsonObject(with: firstDecodedData)
//Code below is an example of using decoded result.
guard let resultArray = secondDecoded as? [[String: Any]] else {
print("result is not an Array of Dictionary")
break block
}
print(resultArray)
if
let addlFields = resultArray[0]["addlFields"] as? [String],
let firstAddl = addlFields.first
{
print(firstAddl)
}
} catch {
print(error)
}
Outputs: (Omitting some output for print(responseData as NSData).)
[["id": a1v24000000uOrPAAU, "value": International University "MITSO", "addlFields": <__NSSingleObjectArrayI 0x100e40c80>(
Mi?narodny Universitet "MITSO"
)
]]
Mi?narodny Universitet "MITSO"
(You may find some parts like <__NSSingleObjectArrayI 0x100e40c80> are strange, but it's just a problem of generating default description and you can access the elements as an Array.)
Anyway, please try and see what you can get with my code above.
#OOPer thank you for the solution. Appreciate you giving your time.
Solution worked as expected. Pasting code here which may help others.
Here is how I am doing -
func getData(text:String, callback:#escaping (_ result: Array<somedata>?,_ error:Error?) -> Void) {
let params = ["search":text]
getDataSomeAPI(url: "http:\\xyz.com\fdf", params: params) { (result, error) in
if error == nil {
do {
//Response is double encoded
if let firstDecoded = try JSONSerialization.jsonObject(with: result as! Data, options: .allowFragments) as? String
{
let firstDecodedData = firstDecoded.data(using: .utf8)!
if let secondDecoded = try JSONSerialization.jsonObject(with: firstDecodedData) as? NSArray {
var array = [somedata]()
for obj in secondDecoded {
Mapper<somedata>().map(JSONObject: obj).then { mappedObj in
array.append(mappedObj)
}
}
callback(array,nil)
}
}
}
catch {
//Handle unexpected data format
let error = NSError(domain: "",
code: 0,
userInfo: nil)
let sErr = Error(err: error)
callback(nil, sErr)
}
} else {
callback(nil, error)
}
}
}

iOS Swift:"JSON text did not start with array or object and option to allow fragments not set."

When I converting Json string to dictionary in swift I got the Issue:Error Domain=NSCocoaErrorDomain Code=3840 "JSON text did not start with array or object and option to allow fragments not set." UserInfo={NSDebugDescription=JSON text did not start with array or object and option to allow fragments not set.}
I don't know to fix the issue please give idea for fix the issue.Here I gave my code what i am tried..
The method for converting Json string to dictionary is,
func convertToDictionary(from text: String) throws -> [String: String] {
guard let data = text.data(using: .utf8) else { return [:] }
let anyResult: Any = try JSONSerialization.jsonObject(with: data, options: [])
return anyResult as? [String: String] ?? [:]
}
The Json String is: "[{\"propertyId\":\"1\",\"inspectionTemplateId\":1118,\"value\":[{\"widgetControllerId\":141,\"value\":\"Flood Summary Name\"},{\"widgetControllerId\":142,\"value\":\"Did the property flood?\"},{\"widgetControllerId\":143,\"value\":\"no\"}]}]"
And the Usage of method was:
let jsonString = NSString(data: responseObject as! Data, encoding: String.Encoding.utf8.rawValue)!
print(jsonString)
do {
let dictionary:NSDictionary = try self.convertToDictionary(from: jsonString as String) as NSDictionary
print(dictionary)
} catch {
print(error)
}
Read the error gentleman. Error is 'allow fragments not set'.
Just Just just set .allowFragments.
That's it. (Make sure response is not malformatted)
JSONSerialization.jsonObject(with: data!, options: .allowFragments)
You can try this:
let str = "[{\"propertyId\":\"1\",\"inspectionTemplateId\":1118,\"value\":[{\"widgetControllerId\":141,\"value\":\"Flood Summary Name\"},{\"widgetControllerId\":142,\"value\":\"Did the property flood?\"},{\"widgetControllerId\":143,\"value\":\"no\"}]}]".utf8
let json = try! JSONSerialization.jsonObject(with: Data(str), options: [])
print(json)
This type of issue can also occur if you have a misconfigured server or your server is unreachable. If you receive this type of error from JSON deserialization you may try converting the data to a string and print it out. It may reveal an error like "502 Bad Gateway"

Swift :: Traverse a json string

Here is a string looks like a json.
let text2 = " [{ \"insertion_date\" :\""+"2015-07-31 11:21:04 +0000"+"\",\"mood\": \""+"Happy"+"\",\"temperature\": \""+"22"+"\"},{ \"insertion_date\" :\""+"2015-07-31 11:21:04 +0000"+"\",\"mood\": \""+"Sad"+"\",\"temperature\": \""+"22"+"\"}]"
I can access the whole string like this.
var data = text2.dataUsingEncoding(NSASCIIStringEncoding, allowLossyConversion: false)
var localError: NSError?
var json3: AnyObject! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError)
println(json3)
But now I want to access individual element of this string, like - I want to access the second "mood" key, which has the value "Sad".
How can I access it?
You could use something like this:
var jsonArr: NSArray! = NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers, error: &localError) as! NSArray
for dict in jsonArr {
print(dict.objectForKey("mood"))
}
Which will print each of the moods.
Swift's native JSON parsing is a pain due to all the optional unwrapping:
if let days = json3 as? NSArray {
if let secondDay = days[1] as? NSDictionary {
let mood = secondDay["mood"] as! String
print(mood)
}
}
You can use other framework like SwiftyJson for an easier time.
You can also use Swift's native types instead of NSArray and NSDictionary, and cast the result of NSJSONSerialization as an array of dictionaries ([[String:AnyObject]]):
if let json3 = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: &localError) as? [[String:AnyObject]] {
for dict in json3 {
if let mood = dict["mood"] as? String {
println(mood)
}
}
}
Note also that you don't need to give specific options to NSJSONSerialization, you can pass nil in this case.
If you're absolutely certain that your values will always be of type String, you can also cast the result directly (and avoid later casts when accessing values):
if let json3 = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: &localError) as? [[String:String]] {
for dict in json3 {
if let mood = dict["mood"] {
println(mood)
}
}
}