Deserialization of JSON in swift 5 with JSONSerialization.jsonObject - json

I'm trying to deserialize a json object in swift. The JSON looks like this:
let data = """
{
"code": 200,
"message": "OK",
"results": [
{ "id": 111, "name": "Tony"},
{ "id": 112, "name": "Bill"},
{ "id": 112, "name": "John"}
]
}
""".data(using: .utf8)!
I'm using this to deserialize the JSON
var json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
print (json!["code"]!)
print (json!["message"]!)
print (json!["results"]!)
The correct values are printed in each case, but I can not figure out how to iterate across the value returned by
json!["reults"]
The error message is:
Type 'Any' does not conform to protocol 'Sequence'
Added- after first answer
First answer solves this problem. However, I'm following code from Apple Deveoper's site they do this:
let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
for case let result in json["results"] {
if let restaurant = Restaurant(json: result) {
restaurants.append(restaurant)
}
}
And the result they pass in is a String, is this just an old example? Can I continue down this path?

You have to downcast the value of results to an array ([]) of dictionaries ({}). Then iterate the array
let results = json!["results"] as! [[String:Any]]
for item in results {
print(item["name"] as! String, item["id"] as! Int)
}
Side note: In Swift 4+ the Codable protocol is the better choice

let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] ?? [:]
if let dictJson = json { //If json is not nil
if let arrResults = dictJson["results"] as? Array<Dictionary<String,Any>>{ //If results is not nil
for result in arrResults {
print(result) //Prints result
}
}
}
Try this. It will iterate result.

Related

Parsing a specific content out of a dictionary in a json file

I'm getting the error "Cannot cast value of type dictionary to array" when I run my code. I'm tryna parse from a json file but its not working. Please help.
Here's the json file and i'm specifically tryna print out just the name values:
{
"data": [
{
"id": "2f4628f7ef6dce33d856121f",
"name": "Top products",
"description": "This category contains all the top products on"
},
{
"id": "3f4628f7ef6dce33d856121d",
"name": "Trending products",
"description": "This category contains all the trending products"
}
],
"page": 0,
"size": 2,
"count": 2,
"status": 200,
"isSuccess": true
}
func fetchFrontPageSections() {
let urlString = APIConstants.baseurl + APIConstants.frontpageSections
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers)
for dictionary in jsonData as! [[String: AnyObject]] {
print(dictionary["name"])
}
} catch let jsonError {
print(jsonError)
}
}.resume()
}
When I print the dictionary I'm getting that whole son file in my console which is fine but Im expecting to get a result like:
name: Top Products
name: Trending Products
In your constant jsonData you don't have an array, it's a dictionary as your json file explicitly show it. It contains multiple keys such as data, page, size, ...
You shoud access first the value related to the key data that contains an at this point the array of dictionary that you are looking for.
Parsing Json in Swift has never been easier since Codable.
here's an Excellent tutorial from the great guys at Ray Wenderlich: https://www.raywenderlich.com/3418439-encoding-and-decoding-in-swift
for your specific problem try this:
struct Info: Decodable {
let id: String
let name: String
let description: String
}
struct Dictionary: Decodable {
let data: [Info]
let page: Int
let size: Int
let count: Int
let status: Int
let isSuccess: Bool
}
let decoder = JSONDecoder()
let dictionary = try decoder.decode(Dictionary.self, from: json)

Downloading JSON into Table View in Swift 3

i want to show my data in tableview, i have a json data and i have watched this tutorial. It's working but my json data doesn't have array key "actor".
this is example's json data.
{
"actors": [
{
"name": "Brad Pitt",
"description": "William Bradley 'Brad' Pitt is an American actor and film producer. He has received a Golden Globe Award, a Screen Actors Guild Award, and three Academy Award nominations in acting categories",
"dob": "December 18, 1963",
"country": "United States",
"height": "1.80 m",
"spouse": "Jennifer Aniston",
"children": "Shiloh Nouvel Jolie-Pitt, Maddox Chivan Jolie-Pitt",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/brad.jpg"
},
{
"name": "Tom Cruise",
"description": "Tom Cruise, is an American film actor and producer. He has been nominated for three Academy Awards and has won three Golden Globe Awards. He started his career at age 19 in the 1981 film Endless Love.",
"dob": "July 3, 1962",
"country": "United States",
"height": "1.70 m",
"spouse": "Katie Holmes",
"children": "Suri Cruise, Isabella Jane Cruise, Connor Cruise",
"image": "http://microblogging.wingnity.com/JSONParsingTutorial/cruise.jpg"
}
]
}
it's my json data.
[
{
"ID": 1,
"name": "Amy"
{
"ID": 2,
"name": "John"
}
]
and it's my code;
func downloadJsonWithURL() {
let url = NSURL(string: urlString)
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!.value(forKey: "actors"))
if let studentArray = jsonObj!.value(forKey: "actors") as? NSArray {
for student in studentArray{
if let studentDict = student as? NSDictionary {
if let name = studentDict.value(forKey: "ID") {
self.noArray.append(name as! String)
}
if let name = studentDict.value(forKey: "name") {
self.adiArray.append(name as! String)
}
}
}
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}
}).resume()
}
i have problem here;
enter image description here
and it will be a problem :/
// my json data doesnt have "actors" ?????
print(jsonObj!.value(forKey: "actors"))
if let studentArray = jsonObj!.value(forKey: "actors") as? NSArray {
for student in studentArray{
if let studentDict = student as? NSDictionary {
Your JSON response is of Array type not the Dictionary. In Swift use native Array instead of NSArray. So you need type cast the jsonObject result to [[String:Any]].
if let array = (try? JSONSerialization.jsonObject(with: data!, options: [])) as? [[String:Any]] {
//Now loop through the array.
for item in array {
if let id = item["ID"] as? Int {
self.noArray.append("\(id)")
}
if let name = item["name"] as? String {
self.adiArray.append(name)
}
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
Note: Instead of using multiple array you need to use single array of custom object or dictionary, So batter if you create one custom class and made array of it or use array of dictionary.
I recoment to use SwiftyJSON: https://github.com/SwiftyJSON/SwiftyJSON. Easy instal with cocoapods: pod 'SwiftyJSON'
import SwiftyJSON
URLSession.shared.dataTask(with: URL(string: "asd")!, completionHandler: {(data, response, error) in
guard error == nil else {
print("there is an error \(error)")
return
}
guard data != nil else {
print("there is no data")
return
}
let json = JSON(data: data)
for actor in json["actors"].array {
self.noArray.append(actor["ID"].int)
self.adiArray.append(actor["name"].string)
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}).resume()
The first json have root element is Dictionary so your code is working for first json format. But the second json that you want to parse having the root elemt is array and you are trying to assign the array into dictionary that's why you are gettin error So just Replace your code with ..
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options:[]) as? NSArray
Change
as! NSDictionary
To
as! [String : Any]
i've fixed problem.. i chanced jsonObj's line and closed actor's line
this;
if let jsonObj = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? NSArray {}
and this
if let actorArray = jsonObj!.value(forKey: "actors") as? NSArray {
for student in jsonObj!{
if let studentDict = student as? NSDictionary {
}
}

How to get values nested within dictionary in NSDictionary?

I apologise if the title is unclear. I am not quite sure how exactly NSDictionary and JSON works.
I am trying to make a simple app using Swift that takes values from a .JSON file, and prints them out to the console. Here is my JSON file:
{
"students": [
{
"name": "Stew Dent",
"age": "10",
"year": 6,
"grades": [
{
"english": "a-plus",
"math": "b-plus"
},
{
"english": "a",
"math": "a-minus"
}
]
},
{
"name": "Mary Brown",
"age": "14",
"year": 10,
"grades": [
{
"english": "a-plus",
"math": "a-plus"
},
{
"english": "a-plus",
"math": "a"
}
]
}
]
}
Currently, I am able to retrieve the values of everything except the values in "grades". Here is my Swift code:
if let path = Bundle.main.path(forResource: "school", ofType: "json")
{
do {
// Retrieve data from JSON file
let jsonData = try NSData(contentsOfFile: path, options: .mappedIfSafe)
// Convert data into NSDictionary
let jsonResult = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
if let students : [NSDictionary] = jsonResult?["students"] as? [NSDictionary]
{
if let grades : [NSDictionary] = students["grades"] as? [NSDictionary]
{
for grade: NSDictionary in grades
{
for (subject, gradevalue) in grade
{
print ("\(subject): \(gradevalue)")
}
}
}
}
} catch
{
}
}
If I am not wrong, the "grades" is a dictionary inside the "students" dictionary. However, it fails to run the above code. On the "if let grades..." line, it gives the error message: "Cannot subscript a value of type '[NSDictionary]' with an index type of 'String'.
So what I am I doing wrong? Is there even any way to retrieve those values? Am I missing something? Any help would be greatly appreciated. Thanks :)
You should use Swift native types Data instead of NSData and Dictionary [String:Any] instead of NSDictionary. You also forgot to iterate through each student. Try like this:
do {
if let dict = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any] {
print(dict)
if let students = dict["students"] as? [[String: Any]] {
print(students)
for student in students {
print(student["name"] ?? "")
if let grades = student["grades"] as? [[String: Any]] {
for grade in grades {
for (subject, gradevalue) in grade {
print ("student:",student["name"] ?? "", "subject:", subject, "grade:", gradevalue)
}
}
}
}
}
}
} catch {
print(error)
}
Try this code, this should work looking at what you are trying to do
if let path = Bundle.main.path(forResource: "student", ofType: "json")
{
do {
// Retrieve data from JSON file
let jsonData = try NSData(contentsOfFile: path, options: .mappedIfSafe)
// Convert data into NSDictionary
let jsonResult = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
if let students = jsonResult?["students"] as? [NSDictionary]
{
for student in students
{
for grades in student["grades"] as! [[String:String]]{
for eachGrade in grades{
print("\(eachGrade.key) : \(eachGrade.value)")
}
}
}
}
}
catch
{
}
}

Crash : Convert dictionary to Json string in Swift 3

I'm trying to convert my swift dictionary to Json string but getting strange crash by saying
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Invalid type in JSON write (_SwiftValue)'
My code:
let jsonObject: [String: AnyObject] = [
"firstname": "aaa",
"lastname": "sss",
"email": "my_email",
"nickname": "ddd",
"password": "123",
"username": "qqq"
]
do {
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// here "decoded" is of type `Any`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:String] {
// use dictFromJSON
}
} catch {
print(error.localizedDescription)
}
Please help me!
Regards.
String is not of type AnyObject. Objects are reference types, but String in swift has value semantics. A String however, can be of type Any, so the code below works. I suggest you read up on reference types and value semantic types in Swift; its a subtle but important distinction and its also different from what you expect from most other languages, where String is often a reference type (including objective C).
let jsonObject: [String: Any] = [
"firstname": "aaa",
"lastname": "sss",
"email": "my_email",
"nickname": "ddd",
"password": "123",
"username": "qqq"
]
do {
let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
// here "jsonData" is the dictionary encoded in JSON data
let decoded = try JSONSerialization.jsonObject(with: jsonData, options: [])
// here "decoded" is of type `Any`, decoded from JSON data
// you can now cast it with the right type
if let dictFromJSON = decoded as? [String:String] {
print(dictFromJSON)
}
} catch {
print(error.localizedDescription)
}

json traverse for Google Books API

I am having issues traversing a JSON retrieved from the Google Books API.
I am able to traverse and print the "id" from "items" but how do I get further down the json to print the "title" from "volumeInfo"?
Any tips or pointers appreciated.
JSON from Google:
{
"kind": "books#volumes",
"totalItems": 555,
"items": [
{
"kind": "books#volume",
"id": "BZXn-3QtQ_UC",
"etag": "Phnt2wzOFMo",
"selfLink": "https://www.googleapis.com/books/v1/volumes/BZXn-3QtQ_UC",
"volumeInfo": {
"title": "Revisiting Stephen King",
"subtitle": "A Critical Companion",
"authors": [
"Sharon A. Russell"
],
Swift Code
let url = NSURL(string: "https://www.googleapis.com/books/v1/volumes?q=stephen+king")
NSURLSession.sharedSession().dataTaskWithURL(url!) { (data, response, error) in
if error != nil {
print(error)
return
}
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers)
if json is [String: AnyObject] {
let items = json["items"] as? [[String: AnyObject]]
for item in items! {
let kind = item["id"] as? String
print(kind)
}
}
} catch let jsonError {
print(jsonError)
}
}.resume()
}
volumeInfo is Dictionary so you need to cast it like [String: AnyObject], and then get the title from that volumInfo Dictionary.
for item in items! {
let kind = item["id"] as? String
print(kind)
if let volumeInfo = item["volumeInfo"] as? [String: AnyObject] {
print(volumeInfo["title"])
}
}