I'm attempting to cast a JSON response in swift to a useable dictionary. This seemed like a simple task, however the JSON response I am getting is formatted strangely, and no matter what I try, I am unable to cast it to a dictionary. All of the google examples I've been able to find assume that the format of the JSON response will be as follows:
{
"someKey": 42.0,
"anotherKey": {
"someNestedKey": true
},
{
"someKey": 42.0,
"anotherKey": {
"someNestedKey": true
}
However, the print response in swift I'm receiving using the code below is formatted as follows:
{assets = (
{
"someKey": 42.0,
"anotherKey": {
"someNestedKey": true
},
{
"someKey": 42.0,
"anotherKey": {
"someNestedKey": true
}
);
}
Here is as far as I was able to get in attempting to cast this data to a dictionary in swift. It adds "assets" as the single key in the dictionary, with the value of that key being the entire rest of the response.
let url = URL(string: "https://\(apiKey):\(password)#\(yourStore).myshopify.com/admin/themes/\(currentThemeID)/assets.json")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error)
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: [.allowFragments, JSONSerialization.ReadingOptions.mutableContainers])
print(jsonResult)
if let dictionary = jsonResult as? [String: [String]] {
print(dictionary)
}
} catch {
print("json processing failed")
}
}
}
}
task.resume()
I'm pretty sure the hang up resides around the presence of the two "parenthesis" and "semi-colon" in the JSON response. I'm unable to find any documentation on how those characters effect the response, or on how to handle them when attempting to down-cast in swift.
Any help would be appreciated!
EDIT:
I've pulled up the JSON response in my browser, and here is the formatting:
{"assets":[{"key":"assets\/1-1.png","public_url":"https:\/\/cdn.shopify.com\/s\/files\/1\/0810\/2125\/t\/22\/assets\/1-1.png?5272098227851596200","created_at":"2016-05-16T16:58:27-05:00","updated_at":"2016-05-16T16:58:27-05:00","content_type":"image\/png","size":9127,"theme_id":124078279}{"key":"templates\/search.liquid","public_url":null,"created_at":"2016-05-16T16:59:04-05:00","updated_at":"2016-05-16T16:59:04-05:00","content_type":"text\/x-liquid","size":2931,"theme_id":124078279}]}
This JSON response does not have the assets = (); portion in it, and is formatted correctly. Somehow my swift code is improperly parsing the data?
Repeatedly cast as [String: Any] to get down to the part of the JSON response you want.
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: [.allowFragments, JSONSerialization.ReadingOptions.mutableContainers])
print(jsonResult)
guard
let dictionary = jsonResult as? [String: Any],
let assetData = dictionary["assets"] as? [String: Any] else {
print("The JSON structure doesn't meet our expectations \(urlContent)")
return
}
print(assetData)
} catch {
print("json processing failed")
}
Related
I send request like this:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
// check for fundamental networking error
print("error=\(String(describing: error))")
completion(nil)
return
}
print("********_Respone status code is: ",(response as! HTTPURLResponse).statusCode)
print("********_Respone url code is: ",response?.url as Any )
do {
let json = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [String: Any]
let res:HTTPURLResponse = response as! HTTPURLResponse
print(json)// yessssssssss goooood
} catch {
completion(nil)
return
}
}
task.resume()
it's working correctly when response is a Dictionary, but when my response is an array show this error:
Could not cast value of type '__NSArrayI' to 'NSDictionary'
Please help me to fix this.
Deserialize the JSON once and omit the options, Array and Dictionary aren't fragmented. Then optional bind the result.
do {
let json = try JSONSerialization.jsonObject(with: data)
if let jsonArray = json as? [[String:Any]] {
print("json is array", jsonArray)
} else if let jsonDictionary = json as? [String:Any] {
print("json is dictionary", jsonDictionary)
} else {
print("This should never be displayed")
}
} ...
If the result is supposed to be only Array or Dictionary then you can force unwrap the result to dictionary and remove the last else clause
do {
let json = try JSONSerialization.jsonObject(with: data)
if let jsonArray = json as? [[String:Any]] {
print("json is array", jsonArray)
} else {
let jsonDictionary = json as! [String:Any]
}
} ...
I'm trying to loop through a multidimensional array/dictionary in Swift which I got from a JSON string. Originally I come from PHP so I'm probably a bit off with my approach.
My issue is that I can't seem to get into a sub-array/dict.
My code so far:
func getJSON(){
let url = NSURL(string: url)
URLSession.shared.dataTask(with: (url as URL?)!, completionHandler: {(data, response, error) -> Void in
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSDictionary {
print(jsonObj!)
let array = jsonObj!.allKeys
for keys in array {
print(keys)
}
OperationQueue.main.addOperation({
//Nothing to do right now
})
}
}).resume()
}
My JSON:
{
"ArrayName1": {
"info": "This is my first array!",
"more": "Even more info!"
},
"ArrayName2": {
"info": "This is my second array!",
"more": "Even more info about the second array!"
}
}
The function prints the key (e.g. ArrayName1) which is good, but how do I get deeper into the array? To print the "info"?
If you are sure it's dictionary in this form [String: [String: Any]], you may want to try this.
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: [String: Any]] {
let array = jsonObj!.allKeys
for key in array {
print(key)
let info = jsonObj[key]["info"] as! String
}
}
First of all don't use NSURL in Swift 3. There is a native struct URL.
Second of all .allowFragments is useless as the JSON is clearly a collection type.
Third of all don't use NSDictionary in Swift, use native type Dictionary.
Fourth of all do the error handling at least the possible error passed by the data task.
All collection types are dictionaries. Use Fast Enumeration to parse keys and values. All keys and values are strings.
func getJSON(){
let url = URL(string: url)!
URLSession.shared.dataTask(with: url, completionHandler: {(data, response, error) -> Void in
if error != nil {
print(error!)
return
}
do {
if let jsonObj = try JSONSerialization.jsonObject(with: data!) as? [String:[String:String]] {
for (key, innerDictionary) in jsonObj {
print(key)
if let info = innerDictionary["info"] {
print("info", info)
}
}
DispatchQueue.main.async {
//Nothing to do right now
}
} else { print("The JSON object is not [String:[String:String]]") }
} catch {
print(error)
}
}).resume()
}
I believe that the jsonArray["success"] is returning to me that it is nil. because when I ran it without the if case it returned that it was nil. But I don't get why its doing that when there is data in the array
if let data = data {
print(String(data: data, encoding: .utf8) ?? "")
do {
if let jsonArray = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any]{
print(jsonArray)
if let success = jsonArray["success"] as? String {
print(jsonArray)
print(success) //BOOKMARK: Success displays that it is null....why?! 10-13-2017, 12:49
print("foo")
}
}
}
catch{
//TODO tell user something didnt work
print(error)
}
}
//handle reading all json
})
Below is what I received when the if case was not implemented:
{"success":0,"message":"User not found or wrong password"}
["message": User not found or wrong password, "success": 0]
nil
foo
I'm calling an api in swift 3. And from the api I'm getting response in JSON String format. How to convert that JSON String to Dictionary or Array in Swift. Also that JSON string contains further array's and dictionaries.
I've tried using EVReflection tool. It's only converting the upper JSON object in dictionary. The nested dictionaries and array's are still in String format.
Try something like this, response is [String: Any] so you must cast to a any type
DispatchQueue.main.async {
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any] {
let j = json as NSDictionary
guard let resp = response as? HTTPURLResponse
else {
// No response from server
return
}
if resp.statusCode == 200 {
// You can put here some callback, this is a data you need
} else {
// Here is some error
}
}
} catch {
// And this is a error from server
}
}
You should receive jsonData which you can directly try JSONSerialization.
If for any reason you receive it as json String then you have to convert that string to jsonData first
// Here you have to convert Json String to jsonData
// if it's not Json String then skip this line
let jsonData = jsonString.data(using: .utf8)
// Try to convert it to json object
do {
let json = try JSONSerialization.jsonObject(with: jsonData, options: []) as! [String : Any]
// now you can try to parse your json
} catch let error as NSError {
print(error.localizedDescription)
}
Or as #Jay mentioned use Swiftyjson
I'm successfully making a POST request and getting data back in return. I can then print that statement in the form of an NSString, after converting of course.
However, I want to save the returned json in usable variables/constants so I can display in the next subsequent screens. I'm having trouble getting various JSON parsing code to work.
I think some of my weaknesses is not understanding the form of 'data', that is returned when I use the NSURLSession. I used code I found elsewhere, and don't quite grasp what the return types are. Is that data from the code below in JSON? NSData?
Anyways, this script works until I start parsing through the data, trying to make sense of how to abstract the array.
If it helps my console returns my print statements (after converting to a NSString) as such:
Optional({
"result": {
"girl": "monica",
"waist": 22.0,
"hair": "Brunette",
"location": "Los Angeles"
}
})
When I try and use the NSJSON serialization framework, the output of that object looks like this, which is supposedly an NSDictionary?:
{
result: {
girl: monica;
waist: 22.0;
hair: Brunette;
location: "Los Angeles";
}
}
A few things confuse me. Why did the quotation marks go away, and what is the optional and extra "result" attribute... Here's my code starting from NSURL request to end of code
let request = NSMutableURLRequest(URL: NSURL(string: "http://localhost:5000")!)
request.HTTPMethod = "POST"
let postString = "color=\(finalDataPassed)&type=\(thirdDataPassed)&hair=\(dataPassed)&location=\(secondDataPassed)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
println("error=\(error)")
return
}
println("response = \(response)")
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding)
println(responseString)
var error: NSError?
let result = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error)
as? NSDictionary
if(error != nil) {
println(error!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: '\(jsonStr)'")
}
else {
if let parseJSON = result {
println(parseJSON)
for item in parseJSON { // loop through data items
let obj = (item as! NSDictionary).objectForKey("result") as! NSDictionary
for (key, value) in obj {
println("Property: \"\(key as! String)\"")
}
}
}
else {
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: \(jsonStr)")
}
}
Optional (they ? in code) means that the it can be nil. Check Swift Basics
The quotes went away because it's a printout of an NSDictionary which does not print out in a JSON format.
Note: To make things safer since you never know what server can return:
if let dataArray = result["result"] as NSArray{
//your code
}
You have an dictionary inside a dictionary:
{
result: <- Key first dictionary
{
girl: monica; <- start of second dictionary
waist: 22.0;
hair: Brunette;
location: "Los Angeles"; <- end of second dictionary
}
}
For a generic approach you can use:
for item in dataArray { // loop through data items
let obj = item.first as! NSDictionary
for (key, value) in obj {
println("Property: \"\(key as! String)\"")
for (key2, value2) in (value as NSDictionary){
println("Property: \"\(key2 as! String2)\"")
}
}
}
In your case because the first dictionary is just one key you can retrieve it directly instead use the first for loop:
for item in dataArray { // loop through data items
let obj = (item.first as! NSDictionary).objectForKey("result") as NSDictionary
for (key, value) in obj {
println("Property: \"\(key as! String)\"")
}
}