Get data out of array of dictionaries from JSON response - ios

I am trying to get data out of an array of nested dictionaries which is the result of a JSON response.
func fetchData() {
let urlString = "https://global.api.pvp.net/api/lol/static-data/na/v1.2/champion?api_key=\(self.apiKey)"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print(error ?? "ERROR!")
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as! [String:Any]
print(parsedData)
print(parsedData["type"] ?? "")
print(parsedData["version"] ?? "")
print(type(of: parsedData))
print(type(of: parsedData["data"]!))
//ERROR HERE.
//"Cannot subscript a value of type '[String, Any]' with an index of type '(String, (String, Any).Type)'
let innerItem = parsedData["Aatrox", (String, Any)]
} catch let error as NSError {
print(error)
}
}
}.resume()
Here is what is printed:
["type": champion, "version": 7.4.3, "data": {
Aatrox = {
id = 266;
key = Aatrox;
name = Aatrox;
title = "the Darkin Blade";
};
Ahri = {
id = 103;
key = Ahri;
name = Ahri;
title = "the Nine-Tailed Fox";
};
Akali = {
id = 84;
key = Akali;
name = Akali;
title = "the Fist of Shadow";
};
Alistar = {
id = 12;
key = Alistar;
name = Alistar;
title = "the Minotaur";
};
}]
champion
7.4.3
Dictionary<String, Any>
__NSDictionaryI
I am trying to get the "id" for "Aatrox".
How would i go about doing this?
is it because the type of parsedData["data"]! is __NSDictionaryI??
Thanks for the help.

First you need to extract data Dictionary then access other dictionary.
do {
let parsedData = try JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
if let passDic = parsedData["data"] as? [String:Any],
let innerItem = passDic["Aatrox"] as? [String: Any] {
print(innerItem)
}
} catch {
print(error)
}

Seems like it should be let innerItem: [String: Any] = parsedData["Aatrox"]

let data : [string:Any] = parsedData.value(key: "data")
let aatrox : [string:Any] = data["Aatrox"]
print("**id**\(aatrox["id"])")
this thing work for me..

Related

How to parse a api for swift 3?

Have been researching on the parsing for quite a bit. With plethora of information avilable for JSON nothing seems to explain how to do in a sensible way to extract information with swift 3.
This is what got so far
func getBookDetails() {
let scriptUrl = "https://www.googleapis.com/books/v1/volumes?q=isbn:9781451648546" .
let myurl = URL(string:scriptUrl)
let request = NSMutableURLRequest(url: myurl!)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: myurl! ) { (data, response, error) in
if error != nil{
print("THIS ERROR",error!)
return
} else{
if let mydata = data{
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let dictonary = myJson["items"] as AnyObject? {
print("the DICTONARY",dictonary) // ----> OUTPUT
if let dictonaryAA = dictonary["accessInfo"] as AnyObject? {
print("the accessInfo",dictonaryAA)
}
}
} catch{
print("this is the in CATCH")
}
} //data
}
}
task.resume()
}
}
OUTPUT :
the DICTONARY (
{
accessInfo = {
accessViewStatus = SAMPLE;
country = US;
=============
RELEVANT DATA as in https://www.googleapis.com/books/v1/volumes?
q=isbn:9781451648546"
==========================
title = "Steve Jobs";
};
}
)
Just need to parse through the json data to get the name, author and title of the book with reference to isbn.
Know there should be a better way to do things that is easily understandable to someone new into the language
You can parse the api in two ways
Using URLSession:
let rawDataStr: NSString = "data={\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"
self.parsePostAPIWithParam(apiName: "get_posts", paramStr: rawDataStr){ ResDictionary in
// let statusVal = ResDictionary["status"] as? String
self.postsDict = (ResDictionary["posts"] as! NSArray!) as! [Any]
print("\n posts count:",self.postsDict.count)
}
func parsePostAPIWithParam(apiName:NSString, paramStr:NSString,callback: #escaping ((NSDictionary) -> ())) {
var convertedJsonDictResponse:NSDictionary!
let dataStr: NSString = paramStr
let postData = NSMutableData(data: dataStr.data(using: String.Encoding.utf8.rawValue)!)
let request = NSMutableURLRequest(url: NSURL(string: "http://13.12..205.248/get_posts/")! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = nil
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
print(error as Any)
} else {
let httpResponse = response as? HTTPURLResponse
print(httpResponse as Any)
do{
if let convertedJsonIntoDict = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
convertedJsonDictResponse = convertedJsonIntoDict.object(forKey: apiName) as? NSDictionary
// callback for response
callback(convertedJsonDictResponse)
}
} catch let error as NSError {
print(error)
}
}
Using Alamofire
func AlamofirePOSTRequest() {
let urlString = "http://13.12..205.../get_posts/"
let para = ["data": "{\"mobile\":\"9420....6\",\"password\":\"56147180..1\",\"page_no\":\"1\"}"]
Alamofire.request(urlString, method: .post, parameters: para , headers: nil).responseJSON {
response in
switch response.result {
case .success:
print("response: ",response)
let swiftyJsonVar = JSON(response.result.value!)
if let resData = swiftyJsonVar["posts"].arrayObject {
self.postsDict = resData as! [[String:AnyObject]]
}
print("\n \n alomafire swiftyJsonVar: ",swiftyJsonVar)
break
case .failure(let error):
print(error)
}
}
}
})
dataTask.resume()
}
First of all, all JSON types are value types in Swift 3 so the most unspecified type is Any, not AnyObject.
Second of all, there are only two collection types in the JSON type set, dictionary ([String:Any]) and array ([Any], but in most cases [[String:Any]]). It's never just Any nor AnyObject.
Third of all, the given JSON does not contain a key name.
For convenience let's use a type alias for a JSON dictionary:
typealias JSONDictionary = [String:Any]
The root object is a dictionary, in the dictionary there is an array of dictionaries for key items. And pass no options, .mutableContainers is nonsense in Swift.
guard let myJson = try JSONSerialization.jsonObject(with: mydata) as? JSONDictionary,
let items = myJson["items"] as? [JSONDictionary] else { return }
Iterate through the array and extract the values for title and authors which is an array by the way. Both values are in another dictionary for key volumeInfo.
for item in items {
if let volumeInfo = item["volumeInfo"] as? JSONDictionary {
let title = volumeInfo["title"] as? String
let authors = volumeInfo["authors"] as? [String]
print(title ?? "no title", authors ?? "no authors")
The ISBN information is in an array for key industryIdentifiers
if let industryIdentifiers = volumeInfo["industryIdentifiers"] as? [JSONDictionary] {
for identifier in industryIdentifiers {
let type = identifier["type"] as! String
let isbn = identifier["identifier"] as! String
print(type, isbn)
}
}
}
}
You are doing wrong in this line
if let dictonaryAA = dictonary["accessInfo"] as AnyObject?
because dictonary here is an array not dictionary. It is array of dictionaries. So as to get first object from that array first use dictonary[0], then use accessInfo key from this.
I am attaching the code for your do block
do{
let myJson = try (JSONSerialization.jsonObject(with: mydata, options: JSONSerialization.ReadingOptions.mutableContainers)) as AnyObject
// print("this is the MY JSON",myJson) ---> prints out the json
if let array = myJson["items"] as AnyObject? {
print("the array",array) // ----> OUTPUT
let dict = array.object(at: 0) as AnyObject//Master Json
let accessInf = dict.object(forKey: "accessInfo") //Your access info json
print("the accessInfo",accessInf)
}
}
Hope this helps you.

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

JSON Parsing & Optionals in Swift 3

So i recently updated to Swift 3/XCode 8 and some of my code went hay-wire. I've read that some syntax changes have been made but I can't seem get this one right.
I make a request to Twitter and get JSON back:
func forLoadStats(completion: (AnyObject?, NSError?) -> Void)
{
var clientError: NSError?
let idString = api.getUserID()
let client = TWTRAPIClient()
let request = client.urlRequest(withMethod: "GET", url: "https://api.twitter.com/1.1/users/show.json", parameters: ["user_id" : 27446437], error: &clientError)
client.sendTwitterRequest(request)
{ (response, data, connectionError) in
if (connectionError == nil)
{
do {
if let json: Any = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [AnyObject]
{
if let json = json, let immage = json?["profile_image_url_https"] as? String
{
//Make ProfilePic edges round
self.profPic.layer.cornerRadius = 42
self.profPic.clipsToBounds = true
//let immage = image["profile_image_url_https"] as String
let _vImageUrl = immage.replacingOccurrences(of: "_normal", with: "")
let urlProfilePic = NSURL(string: _vImageUrl)
let urlPP = NSData(contentsOf: urlProfilePic! as URL)
self.profPic.image = UIImage(data: urlPP! as Data)
let ScrName = json["screen_name"] as! String
self.scrNameLabel.text = "#\(ScrName)"
//Populate Followers Label.text
let flwrVar = json["followers_count"] as! Int
self.followerLbl.text = "\(flwrVar)"
//Populate Following Label.text
let flwngVar = json["friends_count"] as! Int
self.followingLbl.text = "\(flwngVar)"
//Populate Bio
let bio = json["description"] as! String
self.bioLabel.text = "\(bio)"
//created at date
let accountAge = json["created_at"] as! String
self.createdLbl.text = "\(accountAge)"
let tweetCount = json["statuses_count"] as! Int
self.tweetCount.text = "\(tweetCount)"
let likes = json["favourites_count"] as! Int
self.likesCount.text = "\(likes)"
let lists = json["listed_count"] as! Int
self.listedCount.text = "\(lists)"
}
}
}
catch let error
{
print(error)
}
}
}
}
I get an error on the second "If let" statement that says: "initializer for conditional binding must have optional type not 'Any.
Can someone explain why this is?
Your JSON is obviously a dictionary, a JSON dictionary in Swift 3 is [String:Any]
You caused the error by the silly Any annotation (which is supposed to be Any? but is practically nonsensical) because it confuses the compiler.
If you use a do block, try without question mark but use optional binding:
...
if let json = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any] {
if let immage = json["profile_image_url_https"] as? String { ...
There are a couple of problems with the syntax. [AnyObject] will not work to use reference items such as json["profile_image_url_https"]. also, you are redeclaring json through let json = json, which makes it a non option in your let immage = json?["profile_image_url_https"] call, so that becomes a problem. This structure does not have any compiler errors
if let json = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String:Any]
{
if let json = json, let immage = json["profile_image_url_https"] as? String
{
}
}
This does not have any compiler errors.

Cannot convert jsonArray element to integer

do{
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
let arrayJSON = resultJSON as! NSArray
let success:NSInteger = arrayJSON["success"] as! NSInteger
if (success == 1 ) ....
json data is the response from the server, i am trying to convert it to integer but i get the conversation error.
This is a working exmaple (tested on my machine)
let task = session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if let error = error {
print(error)
}
if let data = data{
print("data =\(data)")
do{
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
let resultDictionary = resultJSON as? NSDictionary
let success = resultDictionary!["success"]!
let successInteger = success as! Int
print("success = \(success)")
if successInteger == 1 {
print("yes")
}else{
print("no")
}
}catch _{
print("Received not-well-formatted JSON")
}
}
if let response = response {
print("url = \(response.URL!)")
print("response = \(response)")
let httpResponse = response as! NSHTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
where the response is:
{ "error_message" : "No User", "success" : 0}
Note
you said that your server responnes as:
{ "error_message" = "No User"; success = 0; }
and this is not a valid json, you should correct it to match the json that i gave to you
You're casting resultJSON as an NSArray but then you try to use it as a dictionary by subscripting "success".
If the response is a dictionary, then cast the result as a dictionary:
let result = resultJSON as! NSDictionary
let success = result["success"] as! NSInteger
If the response is an array of dictionaries, then first select one of the items before subscripting.
let arrayJSON = resultJSON as! NSArray
let success = arrayJSON[0]["success"] as! NSInteger
Note: when possible, prefer using Swift's typed arrays an dictionaries rather than Foundation's NSArray and NSDictionary. Also you should avoid force casting with !, it's better to unwrap optionals safely with if let ... = ... as? ... or any other mechanism.
Update
Here's an example:
do {
let resultJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions())
var success = 0
if let dictJSON = resultJSON as? [String:AnyObject] {
if let successInteger = dictJSON["success"] as? Int {
success = successInteger
} else {
print("no 'success' key in the dictionary, or 'success' was not compatible with Int")
}
} else {
print("unknown JSON problem")
}
if success == 1 {
// yay!
} else {
// nope
}
In this example I'm using a Swift dictionary [String:AnyObject] instead of an NSDictionary, and I'm using a Swift integer Int instead of Foundation's NSInteger. I'm also typecasting with if let instead of forcing.

Resources