Parsing json string crashes - ios

I'm trying to parse a json string:
if let jsonStr = asd.value(forKey: "orderData") as? String {
print(jsonStr)
let data = jsonStr.data(using: String.Encoding.utf8, allowLossyConversion: false)!
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String: AnyObject] // CRASHES HERE
if let names = json["product_name"] as? [String] {
print(names)
}
} catch let error as NSError {
print("Failed to load: \(error.localizedDescription)")
}
}
But at the line let json = try JSONSeri... it crashes saying Could not cast value of type '__NSArrayI' to 'NSDictionary'.
Also tried changing this as! [String: AnyObject] to as! [[String: AnyObject]]. But it still doesn't work.
This is my json string structure:
[
{
"product_id" : "1",
"category_json" : {
"category_id" : "1",
"category_name" : "nvm"
},
"selling_price" : "200",
"product_name" : "nvm",
},
{
"product_id" : "2",
"category_json" : {
"category_id" : "2",
"category_name" : "cas"
},
"selling_price" : "800",
"product_name" : "cas",
}
]

You should not be force casting with ! unless you are 100% sure that it will succeed.
I would suggest you use the following:
let jsonArray = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [[String: Any]]
This will return you a list of products. If you want a list of the product names than you need to iterate over it and extract the product name of each item. You can do this like so:
let names = jsonArray.map({ $0["product_name"] as? String })

As already mentioned the object is an array, you have to use a for loop to get all items
...
let data = Data(jsonStr.utf8)
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [[String: Any]] {
for item in json {
if let name = item["product_name"] as? String {
print(name)
}
}
}
} catch {
print("Failed to load: \(error.localizedDescription)")
}

Related

iOS - JSONSerialization VS JSONDecoder and usage

So i am getting the follow json :
{
"output": [
"{\"cameraIsOnboarded\" : true}"
],
"exit_code": 0
}
i tried to decode it with the model below:
struct CameraOnboard: Codable {
var output: [Output]
//var exit_code: Int?
}
struct Output: Codable {
var cameraIsOnboarded: Bool?
}
And then use it in my parser :
let infoString = try JSONDecoder().decode(CameraOnboard.self, from: data)
but it craches.
Then i have tried to use JSONSerialization because i thought i have a problem with the \"cameraIsOnboarded\" key, so i got from alamofire result string and tried the follow:
let jsonData = data.data(using: .utf8)
var dic: [String : Any]?
do {
dic = try JSONSerialization.jsonObject(with: jsonData!, options: []) as? [String : Any]
} catch {
print(error.localizedDescription)
}
print(dic!)
if let outPutArr = dic?["output"] as? NSArray {
print(outPutArr)
if let first = outPutArr.firstObject {
print(first)
//let val = first["cameraIsOnboarded"]
// print(val!)
}
}
so as above i dont no how to extract the value yet i print :
{"cameraIsOnboarded" : true}
if i do as follow:
if let first = outPutArr.firstObject as? [String: Bool] {
print(first)
//let val = first["cameraIsOnboarded"]
// print(val!)
}
it dosent step inside.
Thanks
The json should look like ( Recommended)
{
"output": {
"cameraIsOnboarded" : true
},
"exit_code": 0
}
You can use this to handle current case
do {
let dic = try JSONSerialization.jsonObject(with: str.data(using: .utf8)!, options: []) as! [String : Any]
if let outPutArr = dic["output"] as? [String] {
if let first = outPutArr.first {
let dic = try JSONSerialization.jsonObject(with: (first as! String).data(using: .utf8)!, options: []) as! [String : Bool]
print(dic["cameraIsOnboarded"])
}
}
} catch {
print(error)
}

How can I parse an array into the json on Swift?

I need to take the value "maxtemp_c" from the json request, but it didn't work.
This is what I've do, It will block when I try to parse the json and the value is nil
//url of the json
let url = URL(string: "http://api.apixu.com/v1/forecast.json?key=6cffef39633d4489a0e101339171811&q=Loria&days=3")!
let taskfor = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print("some error occured")
} else {
if error == nil {
do{
let jsonResult = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as! [String : AnyObject]
let forecast = jsonResult["forecast"] as? [String : AnyObject]
// I would not recommend to use NSDictionary, try using Swift types instead
guard let forecastday = forecast!["forecastday"] as? [[String:Any]] else { return }
// Check for the weather parameter as an array of dictionaries and than excess the first array's description
var finalArray:[Any] = []
//now I try to parse the json but the result is nil
for result in forecastday {
if let dict = result as? [String: Any], let forecastday = dict["day"] as? [String] {
finalArray.append(forecastday)
}
}
print(finalArray)
}catch {
print("JSON Preocessing failed")
}
}
}
}
taskfor.resume()
and this is the json that I have to parse
"forecast": {
"forecastday": [ //this is the problem that the value is nil
{
"date": "2017-11-26",
"date_epoch": 1511654400,
"day": {
"maxtemp_c": 7.3, //this is the value that I need
Looks like "day" key contains Dictionary not Array, just change it to [String: Any] from [String]
for result in forecastday {
if let dict = result as? [String: Any], let forecastday = dict["day"] as? [String: Any] {
finalArray.append(forecastday)
}
}
If you want just "maxtemp_c" array then declare final array as [Double] and add a check for "maxtemp_c" in the end
if let forecastday = forecast["forecastday"] as? [[String: Any]] {
var finalArray: [Double] = []
for result in forecastday {
if let dict = result as? [String: Any], let forecastday = dict["day"] as? [String: Any], let temp = forecastday["maxtemp_c"] as? Double {
finalArray.append(temp)
}
}
print(finalArray)
}
If you want "maxtemp_c" in [String] format then do
finalArray.append(String(format: "%.1f", temp))

how to get the JSONArray from jsonObject in Swift 3.1

{
"status": true,
"status_code": 1,
"content": [
{
"cat_id": "3",
"cat_name": "Food",
"cat_parentid": "2"
},
{
"cat_id": "4",
"cat_name": "Entertainment",
"cat_parentid": "2"
},
{
"cat_id": "5",
"cat_name": "Cars",
"cat_parentid": "2"
},
{
"cat_id": "12",
"cat_name": "Personal Care",
"cat_parentid": "2"
}
],
"message": "Success"
}
UPDATE
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
completion((json as? AnyObject)!) //here completion callback will return the jsonObject to my UIViewController.
}
} catch let error {
print(error.localizedDescription)
}
this is my JSONObject. I am very new to the swift. how to get the content JSONArray and further process in swift.? Anybody can help me? Help will be appreciated.
This code checks if the status is true, gets the array for key content and prints all values in the array.
The array is clearly [[String:String]] so cast the object to this specific type.
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
if let status = json["status"] as? Bool, status == true {
if let content = json["content"] as? [[String:String]] {
for category in content {
let id = category["cat_id"]
let name = category["cat_name"]
let parentId = category["cat_parentid"]
print(id , name, parentId)
}
}
}
}
} catch let error {
print(error.localizedDescription)
}
PS: As always, never use .mutableContainers in Swift. It's meaningless
Check whether your json has content array
if let content = json["content"] as? [Dictionary<String, AnyObject>] {
print(content) // it will give you content array
}
Get content array like this:
let allContent = json["content"] as? [[String: Any]]
Full sample:
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
if let allContent = json["content"] as? [[String: Any]] {
for content in allContent {
let catId = content["cat_id"] as? String
let catName = content["cat_name"] as? String
let catParentId = content["cat_parentid"] as? String
print(">> catid=" + catId!)
print(">> catName=" + catName!)
print(">> catparentID=" + catParentId!)
}
}
}
} catch let error {
print(error.localizedDescription)
}
let content = dict.objectForKey("content")! as NSArray
Then you can get json of single object for parsing by
for var cat in content
{
print(cat)
}
Another alternative way, by using the library.
First, import JSON library for Swift - SwiftyJSON and use the code:
import SwiftyJSON
let json = JSON(<jsonObject data>)
let contentArray: Array<JSON> = json["content"].arrayValue
Library Integration
If you're using cocoapods then use this pod:
pod 'SwiftyJSON'
OR else just drag SwiftyJSON.swift to the project tree.
you can extract you data by providing key
if let array = result["content"] as? Array<AnyObject> {
print(arry)
}
You can access like below
if let filePath = Bundle.main.path(forResource: "sample", ofType: "json"), let data = FileManager().contents(atPath: filePath) {
do {
let dicRes = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
let contentArray = dicRes?["content"]
print("contentArray == \(contentArray)")
} catch {
}
}

What is the different between these two rows in the JSON array which means one isn't being recognised?

I have an API call that retrieves an array from my database featuring countries, divisions, teams.
This output shows the format of the JSON when received in Swift (teams not shown in example:
Optional(["countries": <__NSArrayI 0x6180001f6500>(
{
id = 1;
name = Denmark;
},
{
id = 2;
name = Belgium;
}
)
, "divisions": <__NSArrayI 0x7fc8a34455a0>(
{
Name = "ALKA SUPERLIGA";
"country_id" = 1;
id = 1;
},
{
Name = "PRO LEAGUE";
"country_id" = 2;
id = 2;
}
I am using the following function in swift to sort the countries and divisions into different string arrays to then use in an UITableView.
override func viewDidAppear(_ animated: Bool) {
let myUrl = URL(string: "http://www.quasisquest.uk/KeepScore/getTeams.php?");
var request = URLRequest(url:myUrl!);
request.httpMethod = "POST";
let postString = "";
request.httpBody = postString.data(using: String.Encoding.utf8);
let task = URLSession.shared.dataTask(with: request) { (data: Data?, response: URLResponse?, error: Error?) in
DispatchQueue.main.async
{
if error != nil {
print("error=\(error)")
return
}
do{
let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:AnyObject]
print (json)
if let arr = json?["countries"] as? [[String:String]] {
self.countryId = arr.flatMap { $0["id"]!}
self.countries = arr.flatMap { $0["name"]!}
self.teamsTableView.reloadData()
print ("Countries: ",self.countries)
}
if let arr = json?["divisions"] as? [[String:String]] {
self.divisions = arr.flatMap { $0["Name"]!}
self.divisionId = arr.flatMap { $0["id"]!}
self.teamsTableView.reloadData()
print ("Divisions: ",self.divisions)
}
} catch{
print(error)
}
}
}
task.resume()
//membersTableView.reloadData()
}
The print ("Countries: ",self.countries) outputs as expected, below:
Countries: [Optional("Denmark"), Optional("Belgium")
But the print ("Divisions: ",self.divisions) doesn't even run. So there must be something wrong with that IF command, yet I am using the same code as what I used from the 'countries' section.
Any ideas?
I have tried replacing [[String : String]] with [[String : String?]] but this did nothing. I have also tried [[String : Any]] but this brought the following error on the arr.flapMap lines:
'(_) -> Any' to expected argument type '(Dictionary) -> SegmentOfResult'
First of all in Swift 3 the common unspecified type is Any rather than AnyObject
In the given JSON
The root object is [String:Any]
The value for divisions is [[String:Any]] because it contains String and <null> types.
The value <null> will be replaced with n/a
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any] {
print (json)
if let arr = json["countries"] as? [[String:String]] {
self.countryId = arr.flatMap { $0["id"]!}
self.countries = arr.flatMap { $0["name"]!}
self.teamsTableView.reloadData()
print ("Countries: ",self.countries)
}
if let arr = json["divisions"] as? [[String:Any]] {
self.divisions = arr.flatMap { $0["Name"] as? String ?? "n/a" }
self.divisionId = arr.flatMap { $0["id"] as? String }
self.teamsTableView.reloadData()
print ("Divisions: ",self.divisions)
}
}
Notes:
Do not use multiple arrays as data source in conjunction with flatMap, this is pretty error-prone, use a custom struct instead.
A POST request is not needed, GET (a data task passing the url) is sufficient.
Probably "country_id" key has integer value and because of that "if let arr = json?["divisions"] as? [[String:String]]" statement is not working.
Always try to set [[String:Any]] to any array of dictionary if you retrieving it from server.
Your dictionary is of type [String:Any], thus the cast fails. Use Any:
if let arr = json?["divisions"] as! [[String:Any]] { ...
It happens because in case of divisions some elements contain empty (null) names. Which means that real type is not [[String:String]], but [[String:String?]]

Swift json delete key

I want to parsing my json file without "results" tab my new json file
[
{
"Id": 708,
"Name": "My name",
"ImageUrl": "2016728135316.jpg"
}
Codes under below
private func getMoviesFromJSON(jsonData: NSData) throws -> [Movie] {
var movies = [Movie]()
do {
if let jsonObject = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [String: AnyObject], jsonArray = jsonObject["results"] as? [[String: AnyObject]] {
for i in jsonArray {
var properties = [String: AnyObject]()
properties[JSONKeys.id] = i[JSONKeys.id]
properties[JSONKeys.title] = i[JSONKeys.title]
properties[JSONKeys.posterPath] = i[JSONKeys.posterPath]
let movie = Movie(properties: properties)
movies.append(movie)
}
}
} catch {
throw TMDBErrors.ParsingError
}
return movies
}
I think must be change this line or must be delete.
jsonObject["results"]
I need your help , Thank You !
Your json doesn't have any results parameter .. so you don't need it at all ..
do {
if let jsonArray = try NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments) as? [[String: AnyObject]] {
for i in jsonArray {
var properties = [String: AnyObject]()
properties[JSONKeys.id] = i[JSONKeys.id]
properties[JSONKeys.title] = i[JSONKeys.title]
properties[JSONKeys.posterPath] = i[JSONKeys.posterPath]
let movie = Movie(properties: properties)
movies.append(movie)
}
}
} catch {
throw TMDBErrors.ParsingError
}

Resources