Parsing JSON using Swift 3 - json

I have this json data that I want to consume in Swift 3. I'm learning Swift and building a very basic app that displays the list of items in tableUIView from JSON.
{
"expertPainPanels" : [
{
"name": "User A",
"organization": "Company A"
},
{
"name": "User B",
"organization": "Company B"
}
]
}
I'm trying to get this data using Swift 3.
if (statusCode == 200) {
do{
let json = try? JSONSerialization.jsonObject(with: data!, options:.allowFragments) // [[String:AnyObject]]
/*
If I do this:
let json = try? JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String:Any]
if let experts = json?["expertPainPanels"] as! [String: Any] {
I get "Initializer for conditional binding must have Optional type, not '[String: Any]'"
*/
// Type 'Any' has no subscript members.
if let experts = json["expertPainPanels"] as? [String: AnyObject] {
for expert in experts {
let name = expert["name"] as? String
let organization = expert["organization"] as? String
let expertPainPanel = ExpertPainPanel(name: name, organization: organization)!
self.expertPainPanels += [expertPainPanel]
self.tableView.reloadData()
self.removeLoadingScreen()
}
}
}catch {
print("Error with Json: \(error)")
}
}
It was working fine in Swift 2. I updated to Swift 3 which broke the code. I read several SO, but I still have hard time understanding it. I applied some suggestions including JSON Parsing in Swift 3, but I'm still unable to fix the error I'm getting.

As of Swift 3, you need to do a cast early on.
This line:
let json = try? JSONSerialization.jsonObject(with: data!, options:.allowFragments)
Should become this:
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as? [String : AnyObject]
This is because JSONSerialization now returns Any, which does not implement a variation for the [] operator. Make sure you safely unwrap the cast and take the common measures to ensure you don't crash your program.
Edit: Your code should more or less look like this.
let data = Data()
let json = try JSONSerialization.jsonObject(with: data, options:.allowFragments) as! [String : AnyObject]
if let experts = json["expertPainPanels"] as? [String: AnyObject] {

Related

how to get particular json value in swift

I want the size value in json object but the problem is I'm getting whole json data i want only size value to print
here is my json
[{
size = {
height = 20
width = 10
},
number = 100
}]
here is my code
do{
let Json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
//print(Json as Any)
guard let newValue = Json as? [[String: Any]] else {
print("invalid format")
return
}
print(newValue)
}catch {
print("Response Not Found.")
}
Please learn to read JSON, it's pretty easy, there are only two collection types:
[] is array, Swift [Any] but in almost all cases [[String:Any]], accessed with index subscription.
{} is dictionary, Swift [String:Any], accessed with key subscription
Never use the mutableContainers option in Swift, it has no effect at all.
if let json = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] {
for item in json {
if let size = item["size"] as? [String:Any] {
print(size["height"], size["width"])
}
}
}
And variable names are supposed to start with a lowercase letter.
PS: You have to cast the type of height and width. The output – which is actually not JSON – is ambiguous, you cannot see if the value is String or Int
You just need to extract size from newValue . Try this
do {
let Json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
guard let newValue = Json as? [[String: Any]],
let size = newValue[0]["size"] as? [String:Any] else {
return
}
print(size)
}
catch {
print("Response Not Found.")
}
guard let newValue = Json as? [[String: Any]] else {
print("invalid format")
return
}
print(newValue["size"])
or if you want height & width
var sizeDict = newValue["size"] as! [String:Any]
print("Width - \(sizeDict["width"])")
print("Width - \(sizeDict["height"])")

Parsing JSON Data in Swift from Guidebox API

I'm trying to parse JSON Data using Swift from Guidebox. An example of the data is like this:
{
"results": [
{
"id": 14673,
"title": "The Golden Girls",
"alternate_titles": [
"Golden Palace"
],
"container_show": 0,
"first_aired": "1985-09-14",
"imdb_id": "tt0088526",
"tvdb": 71292,
"themoviedb": 1678,
"freebase": "\/m\/01fszq",
"wikipedia_id": 217200,
"tvrage": {
"tvrage_id": 5820,
"link": "http:\/\/www.tvrage.com\/shows\/id-5820"
},
"artwork_208x117": "http:\/\/static-api.guidebox.com\/120214\/thumbnails_small\/14673-9570342022-208x117-show-thumbnail.jpg",
"artwork_304x171": "http:\/\/static-api.guidebox.com\/120214\/thumbnails_medium\/14673-3759246217-304x171-show-thumbnail.jpg",
"artwork_448x252": "http:\/\/static-api.guidebox.com\/120214\/thumbnails_large\/14673-2249565586-448x252-show-thumbnail.jpg",
"artwork_608x342": "http:\/\/static-api.guidebox.com\/120214\/thumbnails_xlarge\/14673-6064109057-608x342-show-thumbnail.jpg"
}
],
"total_results": 1,
"development_api_key": "You are currently using a temporary development API key. This API key is for testing only. You have used 57 of 250 available calls. Please see the API docs (http:\/\/api.guidebox.com\/apidocs) for additional information or to generate a production API key."
}
It seems that for my case, the simplest way to use the data would be to convert it to [String: Any], as all I really need is "id", "title", and an artwork value. However, all of the (countless) methods I have used are failing because of the fact that "alternate_titles" gets parsed as an NSArray and it makes everything more difficult.
So far I've tried variations of this method:
do {
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: Any]
let datafinal = jsonResult["results"] as! [String: Any]
//Fails around here usually, when the data is converted to [String: Any] or [Any:Any] because of the array.
let title = datafinal["title"]
} catch {
print("JSON Preocessing failed")
}
I've also used the SwiftyJSON library to try to convert the data to more easily readable JSON, but the methods to pull a dictionary from it always fail (I'm guessing due to the structure as well). Anyone have a simple method to get the JSON data from the URL and easily access the values in "results"? Any help is much appreciated!
Try this
do {
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: Any]
let datafinal = jsonResult["results"] as! NSArary
let title = datafinal[0].value(forKey: "title")
print("\(title)")
} catch {
print("JSON Preocessing failed")
}
All you need to do is access results as an array, and take the first element of the array, what is a Dictionary.
In the future, here is great tool, using what you can check the structure of your data more conveniently, and it might reveals problem like this faster.
do {
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as! [String: Any]
guard let results = jsonResult["results"] as? [Any], let resultDictinary = results.first as? [String: Any] else {
fatalError("Data structure invalid")
}
let title = resultDictinary["title"]
} catch {
print("JSON Preocessing failed")
}

"Expression Implicity Coerced from 'String?' to Any" JSON Swift 3

Hi I have the below JSON code I would like to parse
"data":{
"date":"November 30th, 2016",
"personality":"Larry",
"comment":"Saw the homie today"
},
I'm doing this in my viewDidLoad
let url=URL(string:"http://IP-ADDRESS/info.php")
do {
let allNotificationsData = try Data(contentsOf: url!)
let allNotication = try JSONSerialization.jsonObject(with: allNotificationsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String : AnyObject]
if let arrJSON = allNotication["notifications"] {
for index in 0...arrJSON.count-1 {
let aObject = arrJSON[index] as? [String: AnyObject]
//let name = aObject?["data"]!
if let jsonResponse = aObject,
let info = jsonResponse["data"] {
// Makes sense to check if count > 0 if you're not sure, but...
let transaction_id: String? = info["personality"] as? String
print(transaction_id)
// Do whatever else you need here
}
Which seems to be fine but console returns below. Not sure while "nil" but I just want it show me "date" in the JSON file itself only in the console. Eventually I'll need it to catch an array of dates, not sure how I'll do that but I'm working on it. Let me know if you know what I'm doing wrong. It has to be something with optional.
Assuming the parent object of data is an array (your code suggests that) you can get all data objects in an array with:
do {
let allNotificationsData = try Data(contentsOf: url!)
let allNotification = try JSONSerialization.jsonObject(with: allNotificationsData, options: JSONSerialization.ReadingOptions.allowFragments) as! [String:Any]
if let arrJSON = allNotification["notifications"] as? [[String:Any]] {
let infoArray = arrJSON.flatMap { $0["data"] }
}
...
}
The benefit of flatMap is it ignores nil values.
If you want to access the comment value of the first item in the array write
let comment = (infoArray[0] as! [String:Any])["comment"] as! String

Strange behaviour of optionals in Swift 3

I have experienced a strange behaviour when parsing JSON data using Swift 3.
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! NSDictionary
let items:[AnyObject] = (json["items"] as? [AnyObject])!
for item in items {
let id:String = item["id"] as! String
print("ID: \(id)")
let info = item["volumeInfo"] as AnyObject
print(info)
let title = info["title"]
print(title)
}
} catch {
print("error thrown")
}
This produces the following output. Notice that info is an optional but if I try to unwrap it it states it is not an optional! The script crashes on let title = info["title"] As a result I can't access the title key. This behaviour has changed since Swift 2.
ID: lbvUD6LUyV8C
Optional({
publishedDate = 2002;
publisher = "Sams Publishing";
title = "Java Deployment with JNLP and WebStart";
})
You can do something like:
do {
let json = try JSONSerialization.jsonObject(with: data!) as! [String: Any]
let items = json["items"] as! [[String: Any]]
for item in items {
let id = item["id"] as! String
let info = item["volumeInfo"] as! [String: Any]
let title = info["title"] as! String
print(id)
print(info)
print(title)
}
} catch {
print("error thrown: \(error)")
}
I might suggest excising the code of the ! forced unwrapping (if the JSON was not in the form you expected, do you really want this to crash?), but hopefully this illustrates the basic idea.
The runtime type of info is Optional<Something>, but the compile time type (as you explicitly cast it) is AnyObject. How is the compiler supposed to know that the AnyObject will happen to be an Optional<Something> unless you tell it (in the form of a cast)?
Try:
let info = item["volumeInfo"] as AnyObject?
Now the compiler knows it's an Optional<AnyObject>, so you can unwrap it.
In Swift 3 the compiler must know the types of all subscripted objects if it's an array or dictionary. AnyObject – which has been changed to Any in Swift 3 – is not sufficient.
Since you know that the value for key volumeInfo is a dictionary cast it accordingly preferably using optional bindings
let info = item["volumeInfo"] as? [String:Any] {
print(info)
let title = info["title"] as! String
print(title)
}
This should do:
guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject],
let items = json["items"] as! Array<AnyObject>? else {
return
}
for item in items {
let id = item["id"] as! String
if let info = item["volumeInfo"] as? [String: AnyObject] {
let title = info["title"] as! String
} else {
// do something
}
}

Swift 3 json parsing

I am running into troubles updating my app as Alamofire and SwiftyJSON are not yet supporting Swift 3. I have a url that would return me json as follows:
{
"products": [
{
"body_html":"",
"created_at":"2016-03-02T13:56:18+03:00",
"id":489759251,
"handle":"product",
"options":[
{
"id":627488838,
"product_id":489759251,
"name":"Title",
"position":1,
"values":[
"Default Title"
]
}
],
},
{
"body_html":"",
"created_at":"2016-03-08T05:17:55+03:00",
"id":530420915,
"handle":"product-2",
"options":[
{
"id":6319359750,
"product_id":530420915,
"name":"Title",
"position":1,
"values":[
"Default Title"
]
}
],
},
]
}
I need to be able to parse that json and list all returned products and be able to read any specific attribute and sub options of each.
I checked some other questions here and found several solutions and was able to get the json data and printed it as above. But, I couldn't parse it.
let shopUrl = "https://\(apiKey):\(password)#\(hostname)" + "/admin/products.json"
let url = URL(string: shopUrl)
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
if error != nil {
print(error)
} else {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
print(json)
} catch let error as NSError {
print(error)
}
}
}).resume()
Any help?
To loop over all of the products you need to extract and cast it to the correct type. In this case an array of [String: Any].
I extracted the relevant bit of code and cleaned it up a bit to make this answer more readable.
guard let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any],
let products = json["products"] as? [[String: Any]]
else { return }
for product in products {
guard let id = product["id"] as? Int,
let options = product["options"] as? [[String: Any]]
else { return }
print(id)
print(options)
}
This parses the JSON, the root object is a dictionary, the objects for products and options are arrays. One value respectively is printed as an example.
if let jsonObject = try JSONSerialization.jsonObject(with:data, options: []) as? [String:Any] {
print(jsonObject)
if let products = jsonObject["products"] as? [[String:Any]] {
for aProduct in products {
print(aProduct["created_at"])
if let options = aProduct["options"] as? [[String:Any]] {
for option in options {
print(option["product_id"])
}
}
}
}
}