Parse JSON Data Array with Swift 4 - json

I am using the following code. It only works if the JSON data does not start with a '[' character. It works fine for JSON data starting with a '{' character. There is a similar question here: Parsing JSON array in swift but most of the methods are deprecated and I was unable to get the code to work. Here is the JSON call I am using:
guard let json = (try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [String: Any] else {
print("Error: Could not parse JSON!!!")
return
}
I tried removing all options and using allowFragments and mutableLeaves among others. From what I understand mutableContainers is a default setting but I have been trying whatever I can. Any help or advice would be much appreciated.
Here is a sample of the JSON data I am working with:
{ "CREATED_BY" = "Domain\USER"; "CREATED_DATE" = "2011-09-30T15:00:13"; STATUS = U; "EMPLOYEE_NUMBER" = 039000292; "UPDATED_BY" = "Domain\USER""; "UPDATED_DATE" = "2014-08-02T13:22:01"; }

The issue is that the [] signifies that the json is an Array of objects, so you need to cast this to an array. You can do this by either casting it to [Any] or by casting it to an array of dictionaries (which is what it really is).
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [Any]
let json2 = try JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]]
} catch {
print("Error: Couldn't parse JSON. \(error.localizedDescription)")
}
So provided the following json to the above block:
let jsonString = """
[{
"id": "5",
"name": "Test",
"team1": "thingy team",
"team2": "clicky team",
"category": "4",
"end_date": "1415217600",
"cat_name": "new thingy",
"team1_bets": 1,
"team2_bets": 1
}]
"""
you would end up with an output of the following:
let json = Optional([{
"cat_name" = "new thingy";
category = 4;
"end_date" = 1415217600;
id = 5;
name = Test;
team1 = "thingy team";
"team1_bets" = 1;
team2 = "clicky team";
"team2_bets" = 1;
}])
let json2 = Optional([["team2_bets": 1, "name": Test, "id": 5, "team1_bets": 1, "team2": clicky team, "team1": thingy team, "category": 4, "cat_name": new thingy, "end_date": 1415217600]])
The main difference between the two is that the contents of json are an array of Any objects, which would then need to be cast to whatever data type you're working with. The json2 array is an array of dictionaries, which you would then need to cast the Any objects but you still have the keys available.

Then it may be an array
do {
let json = try JSONSerialization.jsonObject(with: data) as? [Any]
print(json)
}
catch {
print(error)
}
This [ ] means Array ---- > [Any]
while this { } means Dictionary -----> [String:Any]

let json = try JSONSerialization.jsonObject(with: data!) as? [NSDictionary]; //jsonArray
print(json![0].value(forKey: "name")!); //jsonObject

Related

Can' read JSON element in Swift

I'm using Swift 3.0 and cannot seem to parse this JSON response.
{
"books": [{
"name": "NAME",
"key": "Key"
}],
"count": 1
}
Here is what I am using
let booksData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
if let bookCount = booksData["count"] as? String {
print("found")
}
else {
print("Not Found")
}
I must be missing something really obvious here. I'm trying to read count before reading the array of books.
Try this:
if let bookCount = booksData["count"] as? NSNumber {
print("found")
} else {
print("Not Found")
}
In your JSON data, see "count": 1. The value 1 is a JSON number, which is converted to NSNumber with JSONSerialization. And as? casting from NSNumber to String always fails.
if let bookCount = bookData should be if let bookCount = booksData

Parsing JSON using Swift 3

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] {

Get data from json array swift 2

I'm trying to get data drom the json array, this is the code that i'm trying, the thing is that i would like to get only the name that is inside this json
{
"tag": "getuser",
"success": 1,
"error": 0,
"uid": "56108b7e651ad2.95653404",
"user": {
"name": "2",
"phone": "2",
"email": "2"
}
}
I tryied this
let jsonData:NSDictionary = try NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers ) as! NSDictionary
let name = jsonData["user"]
print("Nombre del usuarioes: \(name)")
But this prints the whole user data, name, phone and email, how can i be able to print only the name or only the email?
You don't have to use a library and you don't have to use key-value coding.
The same way you're already using subscripting for your dictionary with this:
let name = jsonData["user"]
you just have to continue subscripting to find your value.
Example:
do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlData!, options: []) as! NSDictionary
let user = jsonData["user"]!
let name = user["name"]
print(name)
} catch {
print(error)
}
Even better with safe unwrapping:
do {
if let data = urlData, let jsonData = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
if let user = jsonData["user"] as? NSDictionary, let name = user["name"] as? String {
print(name)
}
}
} catch {
print(error)
}
Note: in JSON, a dictionary is defined by {} and an array is defined by []. What you have here is a dictionary containing a dictionary, not an array (cf your question title).
A great library to decode json is SwiftyJSON
you can get sub-scripted data from the json like so
import SwiftyJSON
if let dataFromString = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
let name = json["user"]["name"].string
print(name)
}
Use your code, then get the field from jsonData by this:
let name = jsonData.valueForKeyPath("user.name") as! String
let email = jsonData.valueForKeyPath("user.email") as! String

How to parse this specific JSON data in Swift 2.0

I'm trying to parse Json Data from an API :
{
"title": "Mr. Robot",
"first_aired": "2015-06-24",
"network": "USA Network",
"channels": [
{
"id": 12,
"name": "USA",
"short_name": "usa",
"channel_type": "television"
}
],
The Code I'm use is:
var TVArray : [TVInfo] = []
var task : NSURLSessionTask?
func getJSON (urlString: String) {
let url = NSURL(string: urlString)!
let session = NSURLSession.sharedSession()
task = session.dataTaskWithURL(url) {(data, response, error) in
dispatch_async(dispatch_get_main_queue()) {
if (error == nil) {
self.updateJSON(data)
}
else {
}
}
}
task!.resume()
}
func updateJSON (data: NSData!) {
let JSONData = (try! NSJSONSerialization.JSONObjectWithData(data, options: []))
TVArray.removeAll(keepCapacity: true)
if let jsonArray = JSONData {
for j in jsonArray {
let title = jsonResult["title"] as! String
let firstAired = jsonResult["first_aired"] as! String
let network = jsonResult["network"] as! String
let channelName = JsonResult["channels"][0]["name"] as! String
let TV = TVInfo(title: title, firstAired: firstAired, network: network, channelName: channelName)
TVArray.append(TV)
}
}
collectionview.reloadData()
}
}
When I use the above code I get an error 'Initializer for conditional binding must have Optional type, not 'AnyObject'' in front of the line 'if let jsonArray = JsonData'. I've tried some methods I've seen on StackOverflow like the method in the link :
[Parsing JSON in swift 2.0
but it didn't work for me. I'm still a bit new to Swift, I really don't want to use SwiftyJSON. Is this the best way to parse this JSON data or is there a better way of doing it?
Since you've used NSJSONSerialization with try! (note the !, meaning it was forced) the value of JSONData is not an optional: you don't have to unwrap it with if let jsonArray = JSONData.
If you still want an optional value, use try? instead.
Otherwise you could also use try inside a do catch block to handle possible errors.
The type of JSONData is unknown, it needs to be known to be an Array for the following for loop.
use:
let JSONData = try! NSJSONSerialization.JSONObjectWithData(data!, options:[]) as! NSArray
You do not need:
if let jsonArray = JSONData {
because you have already crashed if JSONData is nil from the preceding try!
You are better with:
do {
let jsonArray = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as! NSArray
for j in jsonArray {
// ...
}
} catch {
// handle error
}
Because you have no control over the JSON you receive and crashing because of a server change is not a good idea.
Also really put some time into naming variables, JSONData is not data, it is an array obtained by parsing a JSON string.

what type is this ? Is there an NSList option in Swift?

I've been learning how to work with APIs with Swift lately and I've come into a question of what type of data is this in swift? According to the company's API it's a List[LeagueEntryDto] data type. Here's the output
{"24874695": [{
"queue": "RANKED_SOLO_5x5",
"name": "Twitch's Captains",
"entries": [{
"leaguePoints": 35,
"isFreshBlood": false,
"isHotStreak": false,
"division": "III",
"isInactive": false,
"isVeteran": false,
"losses": 34,
"playerOrTeamName": "AerialStability",
"playerOrTeamId": "24874695",
"wins": 24
}],
"tier": "SILVER"
}]}
How do I get the specific item in "entries"?
Here's my code:
var summonerLeague = NSURL(string: "https://na.api.pvp.net/api/lol/na/v2.5/league/by-summoner/\(summonerID)/entry?api_key=dec19325-8a2c-4377-9e47-4a1fb00a930c")
var dataLeague = NSData(contentsOfURL: summonerLeague!)
if dataLeague != nil {
let summonerLeagueDict = NSJSONSerialization.JSONObjectWithData(dataLeague!, options: NSJSONReadingOptions.AllowFragments, error: &error) as! NSDictionary
println(summonerID)
//print(summonerLeagueDict)
//find summoner League Division
if let summLeague: AnyObject
= summonerLeagueDict["\(summonerID)"] {
//println(summLeague)
// cannot find list
if let summEntry: AnyObject = summLeague[0] {
println(summEntry["tier"]!)
println(summEntry["name"]!)
// nothing comes out of here. This is the part in question
if let summDiv = summEntry["entries"] as? NSDictionary{
println("this is NOT printed")
println(summDiv["division"])
}
}
This is a Dictionary. The key, "24874695", has an Array for value. This array contains a Dictionary, and this Dictionary contains an array, and this array contains a Dictionary. :)
This is a JSON object, so you can follow the collection types knowing that {} is the delimiter for dictionaries and [] is the delimiter for arrays.
A quick example of how to get the first "entries" field:
if let json = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as? [String:AnyObject] {
if let entries = json["24874695"]?[0]["entries"] as? [[String:AnyObject]] {
println(entries)
}
}
Result:
[[playerOrTeamName: AerialStability, isHotStreak: 0, isFreshBlood: 0, wins: 24, playerOrTeamId: 24874695, losses: 34, isInactive: 0, isVeteran: 0, leaguePoints: 35, division: III]]
And you can loop in the data like this:
if let json = NSJSONSerialization.JSONObjectWithData(data!, options: nil, error: nil) as? [String:AnyObject] {
for (key, value) in json as [String:AnyObject] {
println(key)
for item in value as! [[String:AnyObject]] {
println(item["entries"]!)
}
}
}
declaring the type as AnyObject will fail always, you have to cast the object to something more specific, for example (untested)
if let summLeague = summonerLeagueDict["\(summonerID)"] as? Array<AnyObject> {
//println(summLeague)
// cannot find list
if let summEntry = summLeague[0] as? Dictionary<String,AnyObject> {
println(summEntry["tier"] as! String)
println(summEntry["name"] as! String)
// nothing comes out of here. This is the part in question
if let summDiv = summEntry["entries"] as? Dictionary<String,AnyObject>{
println("this is NOT printed")
println(summDiv["division"] as! String)
}
}
}
check with the println()lines where you are