Parsing JSON in swift 2.0 - json

I'm trying to parse JSON data from a server api.
The json :
[
{"interest": {
"title":"Sport",
"sub":[
"painting",
"photography",
"museum"]
}
},
{"interest": {
"title": "music",
"sub": [
"rock",
"classic",
"techno"]
}
}]
The code to get it from a file just to test to parse it :
do{
let path = NSBundle.mainBundle().pathForResource("MainInterest", ofType: "json")
let jsonData = NSData(contentsOfFile: path!)
let jsonResult:NSArray! = try NSJSONSerialization.JSONObjectWithData(jsonData! , options: NSJSONReadingOptions.MutableContainers) as! NSArray
} catch let error as NSError {
print(error)
}
I can access the whole file with :
print(jsonResult)
But I can't access more than one level, here is the first level :
if let a = jsonResult {
print(a[0]["interest"])
}
I can then get my first object from my file, but I would like to access jsonResult[0]["interest"]["title"] (==Sport) or jsonResult[0]["interest"]["sub"][2] (== photography) but I have this kind of error build failed:
Cannot subscript a value of type 'AnyObject?!' with an index of type 'String'
Cannot subscript a value of type 'AnyObject?!' with an index of type 'Int'
Could you please help me ? I'm really stuck and the apple doc is not so explicite on this point I think. And I don't want to use framework like swiftyJson etc.
Thanks a lot !
Ben
EDIT :
Solved with (thanks #maxpovver) :
//To Get Title
let title = (((self.interests as NSArray)[0] as? NSDictionary)?["interest"] as? NSDictionary)?["title"] as? NSString
print(title)
//To Get Sub
let sub = ((((self.interests as NSArray)[0] as? NSDictionary)?["interest"] as? NSDictionary)?["sub"] as! NSArray)[1] as! NSString

The answer
//To Get Title
let title = (((self.interests as Array)[0] as? Dictionary)?["interest"] as? Dictionary)?["title"] as? String
print(title)
//To Get Sub
let sub = ((((self.interests as Array)[0] as? Dictionary)?["interest"] as? Dictionary)?["sub"] as! Array)[1] as! String
Also can be made more readable:
let firstInterest = ((self.interests as Array)[0] as? Dictionary)?["interest"] as? Dictionary)?
let title = firstInterest?["title"] as? String
let sub = (firstInterest?["sub"] as! Array)[1] as! String
You'll never be ablo to simplify that without some extra libraries as swift does not support dynamic types.

Related

How to parse string in swift

My json is like this:
"billerdetails": [
{
"id": "1",
"bname": "ACT Fibernet",
"bcustomerparms": "[{\"paramName\":\"Account Number/User Name\",\"dataType\":\"ALPHANUMERIC\",\"optional\":\"false\",\"minLength\":\"1\",\"maxLength\":\"50\"}]",
"breponseParams": "[{\"amtBreakupList\":[{\"amtBreakup\":\"BASE_BILL_AMOUNT\"}]}]",
......
Here i am able to get bcustomerparms. but here i need bcustomerparms: paramName value like (Account Number/User Name) in one virable.. for that i have written code like below but i am unable to get Account Number/User Name in vairable.
Please help me in below code:
do{
let jsonObj = try JSONSerialization.jsonObject(with: respData, options: .allowFragments) as! [String: Any]
//print("the all make payment json is \(jsonObj)")
let billerdetailsArray = jsonObj["billerdetails"] as! [[String: Any]]
for billerdetail in billerdetailsArray {
self.categoryName = billerdetail["bname"] as? String
var customrParams = billerdetail["bcustomerparms"]
print("biller customrParams \(customrParams)")
}
// here i am getting bcustomerparms
biller customrParams Optional([{"paramName":"Connection ID","dataType":"ALPHANUMERIC","optional":"false","minLength":"8","maxLength":"10"}])
but here i want only paramName value how to get that value. please help me in the above code.
bcustomerparms value is a string not an array of dictionaries , You can try
let customrParams = billerdetail["bcustomerparms"] as! String
let res = try JSONSerialization.jsonObject(with:Data(customrParams.utf8)) as! [[String: Any]]
for item in res {
print(item["paramName"])
}

Get item out of array in Swift

I'm very new to Swift and have spent several hours just trying to pull the photo_url key out of a JSON response.
I'm using this for the reading the JSON:
let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
Then:
if let eventsDictionary = jsonDictionary {
let upcomingEvents = UpcomingEvents(eventsDictionary: eventsDictionary)
completion(upcomingEvents)
} else {
completion(nil)
}
Here is my (failed) attempt to pull out the key:
init(eventsDictionary: [String : Any]) {
//photoUrl = eventsDictionary[EventKeys.photoUrl] as? String
let groups: NSArray = eventsDictionary["groups"] as! NSArray
let url: String = groups[0]
print("THIS IS YOUR RETURNED PHOTO URL--\(url)--END OF RETURNED PHOTO URL")
}
I changed "[String: Any]" to [String: AnyObject] and now i get this...
There are problems casting Any to NSArray. Just make your Init method taking [String:AnyObject]. But, better use Array instead of NSArray here
Try to get url use following code.
let firstObj = groups[0] as! [String: String] // use if let to unwrap is better
let url = firstObj["photo_url"]
To get "photo_url" from the json file in your photo,
it looks like this:
init(eventsDictionary: [String : Any]) {
if let groups = eventsDictionary["groups"] as? [NSDictionary]{
/*
// Get All URL
var urls : [String] = []
for group in groups{
if let url = group.value(forKey: "photo_url"){
urls.append(url)
}
}
*/
// Groups[0] url
let url: String = groups[0].value(forKey: "photo_url") as! String
print("THIS IS YOUR RETURNED PHOTO URL--\(url)--END OF RETURNED PHOTO URL")
}
}
You need to read json as `[String: Any].
if let eventsDictionary = json as? [String: Any] {
let upcomingEvents = UpcomingEvents(eventsDictionary: eventsDictionary)
completion(upcomingEvents)
}
Then, init your UpcomingEvents model like this
init(eventsDictionary: [String : Any]) {
let groups: NSArray = eventsDictionary["groups"] as! NSArray
let group1 = groups[0] as! NSDictionary
let photoURL = group1["photo_url"] as! String
print(photoURL)
}

The JSON output does not contain a key: "value"

I have a JSON parsing issue with my Swift code below. The error I am getting says that my JSON output does not contain a key value.
My code:
Alamofire.request(url, method: .get, headers: headers).responseJSON { (response) -> Void in
let jsonValue = response.result.value as! NSDictionary
if let bpArray = jsonValue["value"] as? [NSDictionary]{
for results in bpArray {...}
Issue:
This conversion doesnt work: if let bpArray = jsonValue["value"] as? [NSDictionary]
My JSON Structure:
{
d: {
results: [
{
__metadata: {},
Key: "AFBWhULFHtKU4j4FhWCmKg==",
ParentKey: "AAAAAAAAAAAAAAAAAAAAAA==",
RootKey: "AFBWhULFHtKU4j4FhWCmKg==",
Partner: "MM-CARR-01",
Type: "2",
Description: "MM Demo Carrier Created for Single Stop / MA",
FrieghtOrder: {}
},
...
Assuming you want to access the results key so try like this:-
if let bpArray = jsonValue["results"] as? [String: AnyObject]{
//yourcode
}
Well your json structure haven't got any key named as value and that's why it's giving an error.
In order to get results array, you first need to get the object in which they are nested and for example 'g' in your case:
if let data = jsonValue["d"] as? [NSDictionary]{
if let resultsArray = data["results"] as? NSArray {
//your code
}
}
Please Use Swifty Json Pod And Try this code
pod 'SwiftyJSON'
In Your file where you get response
import SwiftyJSON
Then After Use This Code
switch response.result {
case .success(let JSON2):
print("Success with JSON: \(JSON2)")
// print("Request failed with error: \(response.response?.statusCode)")
if let response = JSON2 as? NSMutableDictionary
{
}
else if let response = JSON2 as? NSDictionary
{
if let data = response?.object(forKey: "d") as? NSDictionary
{
if let resultsArray = data?.object(forKey: "results") as? NSArray
{
}
}
}
else if JSON2 is NSArray
{
let swjson = JSON(response.result.value!)
print(swjson)
// callback(swjson,nil)
var myMutableDictionary = [AnyHashable: Any]()
myMutableDictionary["myArray"] = swjson
callback(myMutableDictionary as NSDictionary?,nil)
print("accc")
}
else if ((JSON2 as? String) != nil)
{
let userDic : [String : String] = ["quatid":JSON2 as! String]
print(userDic)
}
break
case .failure(let error):
print("Request failed with error: \(error)")
}
Remember it's better we not use NSArray & NSDictionary like things in SWIFT, SWIFT it self providing let, var keyword for various data type.
First you can create your model where you can save the data.
MyModel.swift
class MyModel: NSObject {
var ParentKey : Int?
init(jsonObject:[String:Any]) {
ParentKey = jsonObject["ParentKey"] as? String ?? ""
}
}
Viewcontroller.Swift
var myModel : [MyModel] = []
if let responseData = response["d"] as? [String:Any]{
if let dataObject = responseData["results"] as? [[String:Any]]{
self.myModel = dataObject.map({return MyModel(jsonObject: $0)})//for assigning data into model
}
}
As most people said, I was wrong with "value". Hussain's answer to using [String:AnyObject] Helped.
I am not sure if the below is neat to achieve, but it did the magic. App loaded as expected. Code expert below:
Solution:
if let bpData = jsonValue["d"] as? [String: AnyObject]{
for results in bpData {
let arrayInterim = results.value as? NSArray
for i in 0 ..< arrayInterim!.count {

"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
}
}