Parse nested elements in JSON - json

I couldn't seem to parse the following json:
["data": {
companies = (
);
"login_status" = success;
"rs_customer" = {
id = "<null>";
name = "<null>";
status = "<null>";
};
user = {
email = "email#email.com";
id = 0;
lastlogin = "06/14/16 12:44 am";
name = "Jayson Tamayo";
password = mypassword;
phone = "112345";
};
}, "status": success]
I retrieve that JSON thru:
HTTPGetJSON("http://myurl.com") {
(data: Dictionary<String, AnyObject>, error: String?) -> Void in
if error != nil {
print(error)
} else {
print(data)
let status = data["status"] as? String
print(status)
}
}
When I print the "status" it works. But when I try to use data["name"] I get nil. I also tried data["data"]["name"] but I also get nil.

Your main object is a dictionary.
In the "data" key, there's several values: "companies" is an array, "rs_customer" is a dictionary, the statuses are Strings and "user" is a dictionary.
So, to get the user, you would just have to cast to the proper types, something like this, if data is the object we see in the question:
if let content = data["data"] as? [String:AnyObject] {
if let user = content["user"] as? [String:AnyObject] {
if let name = user["name"] as? String {
print(name)
}
}
}
You can also chain the unwrapping for simpler code:
if let content = data["data"] as? [String:AnyObject],
user = content["user"] as? [String:AnyObject],
name = user["name"] as? String {
print(name)
}

Related

How to convert variable of type Any object to String when this variable contains Null in iOS?

The problem is when I want to cast user variable to string, it says unable to read data. It might be because the user object contains null.
Response:
SUCCESS: {
data = {
user = {
email = "<null>";
firstName = "<null>";
};
//...
};
message = "";
messageCode = "API_200";
}
Code:
if let responseDict = response["data"] as? [String : AnyObject] {
if let user = responseDict["user"] {
if let userString = user as? String
{
var itIsExtracted = true;
}
}
}
What I can see is that your user-object is declared as an Any. Try to set it as a dictionary.
guard let responseDict = response["data"] as? [String : Any],
let user = responseDict["user"] as? [String : Any] else {
// There is no user
return
}
if let userEmail = user["email"] as? String {
print(userEmail)
// Can be a result as "<null>" because of server result
}
if let firstName = user["firstName"] as? String {
print(firstName)
// Can be a result as "<null>" because of server result
}
I can also add what #Vadian said. Blame the owner of the website to send real null values, not a stringified null.

Accessing JSON data with Swift

I have an array of JSON data from the following call:
guard let json = (try? JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers)) as? [Any] else {
print("Not containing JSON")
return
}
when I run print(json) I get the following in the output:
[{
"CREATED_BY" = "DOMAIN\\USER";
"CREATED_DATE" = "2016-11-28T08:43:59";
STATUS = U;
"WIDGET_NUMBER" = K11;
"UPDATED_BY" = "<null>";
"UPDATED_DATE" = "<null>";
}, {
"CREATED_BY" = "DOMAIN\\USER";
"CREATED_DATE" = "2016-05-09T08:46:23";
STATUS = U;
"WIDGET_NUMBER" = 89704;
"UPDATED_BY" = "<null>";
"UPDATED_DATE" = "<null>";
}]
I am trying to get all of the WIDGETNUMBER values in the array of JSON data. The json variable is a Any type and I have not been able to convert to a struct so far. Is there an easy way to get the elements from the JSON objects?
It looks like you have an array of dictionaries
for item in json {
if let item = item as? [String: Any], let widgetNo = item["WIDGET_NUMBER"] {
print(widgetNo)
}
}
Your content is array of Dictionary, so that you must convert each element Dictionary to Json
for dic in content {
do {
let jsonData = try JSONSerialization.data(withJSONObject: dic, options: .prettyPrinted)
print(jsonData)
} catch {
print(error.localizedDescription)
}
}
Or you can read value of WIDGET_NUMBER direct from Dictionary
for dic in content {
print(dic["WIDGET_NUMBER"] ?? "Not found")
}
Joakim's answer is spot on for getting the widget number. For your struct, be sure to add something like this as an initializer to map your object.
let widgetNumber: Int
let user: String
init?(json:[String:Any]) {
guard let widgetNumber = json["WIDGET_NUMBER"] as? Int,
let user = json["CREATED_BY"] as? String else { return nil }
self.widgetNumber = widgetNumber
self.user = user
}
If you just want an array of widget numbers you could use the reduce function which iterates the dictionaries in the array and extracts the widget numbers:
Using your data I put this in a storyboard:
let json = try? JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as! [[String: Any]]
let widgetNumbers = json?.reduce(into: [String]()){ (accum, dict) in
guard let widget = dict["WIDGET_NUMBER"] as? String else { return }
accum.append(widget)
}
widgetNumbers // -> ["K11", "89704"]

Parsing JSON in Swift and accessing values?

I have successfully parsed JSON for:
birthday = "04/10/1986";
id = 202038339983;
location = {
city = Jupiter;
country = "United States";
state = FL;
};
My question is when part of the JSON is:
submissions = {
data = (
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
});}
How am I supposed to access created_time, id, viewer, and message?
I have been able to print the whole submissions JSON response to the console with this code :
guard let jsonD = responseFromServer as? [String : Any] else {return}
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
My console will display:
["data": <__NSArrayI 0x6040001a86c0>(
{
"created_time" = "2018-02-16T05:11:56+0000";
id = "131448394823824_167398094382256";
viewer = "Any random string and/or emojis";
},
{
"created_time" = "2018-02-14T23:36:41+0000";
id = "809809871824_8908987486899";
message = "vday \Ud83d\Udda4\U2665\Ufe0f";
})]
My question is how should I parse the JSON so I can access the created_time inside submissions?
Here is the HTTP Request:
struct XClass: RequestProtocol {
var Path = "/User"
var parameters: [String : Any]? = ["stuff": "id, birthday, location, submissions"]
var aToken = aToken.current
var httpMethod: RequestHTTPMethod = .GET
var apiVersion: APIVersion = .defaultVersion
struct Response: ResponseProtocol {
var id = String()
var birthday = String()
var city = String()
var state = String()
var country = String()
var viewSubs = [String : Any]()
init(XResponse: Any?) {
guard let jsonD = XResponse as? [String : Any] else {return}
id = (jsonD["id"] as? String)!
birthday = (jsonD["birthday"] as? String)!
let XArr = (jsonD["location"] as? [String : String])
city = XArr!["city"]!
country = XArr!["country"]!
state = XArr!["state"]!
let subs1 = (jsonD["submissions"] as? [String : Any])
let accessSubs1 = theSubs1
guard let parsedPost = theSubs1 else {
return
}
viewSubs = theSubs1
}}}
func getXData(){
let connection = RequestConnection()
connection.add(XClass()) { response, result in
switch result {
case .success(let response):
print("Request Succeeded: \(response)\n\n\n")
case .failed(let error):
print("Request Failed: \(error)")
}}
connection.start()
}
Create a struct
struct Data: Decodable {
var created_time : String
var id : String
var viewer : String
}
call to the api url from URLSession
guard let url = URL(string: "your api url")
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error.localizedDescription)
} else {
guard let data = data else {return}
var data: [Data]() = JSONDecoder().decode(Data.self, data)
for dat in data{
print(dat.created_time)
print(dat.id)
print(dat.viewer)
}
}
If you are not using Decodable from Swift 4, or still in Swift 3,
then you can specify that the data in "submissions" is an array of dictionaries (double brackets) then you can iterate that.
Change
let subs1 = (jsonD["submissions"] as? [String : Any])
To
let subs1 = (jsonD["submissions"] as? [[String : Any]])
for sub in subs1 {
let time = sub["created_time "] as? [String : Any]
...
}

Deserialize a Dictionary (JSON) to a swift object

I'm trying to build a login function (POST method) and the resultant is a JSON with user details and few other details. I have created a class with all the fields I need to use from the result of POST call. But I'm facing an issue with deserialzing the json to the object of the class. Can some one help me with this. (I have seen similar questions on SO and tried solving using the solution. I have tried converting the json to string and then to swift object using var UserDetails = UserDetails(json:jsonString)
)
My code:
class UserDetails {
let token:String
let agent_id: Int
let user_id:Int
let company_id:Int
let affliate_Id:Int
let role_id:Int
let username: String
let surname:String
let lastname:String
init(token:String,agent_id: Int,user_id:Int,company_id:Int,affliate_Id:Int,role_id:Int,username: String,surname:String,lastname:String) {
self.token = token;
self.agent_id = agent_id;
self.user_id = user_id;
self.company_id = company_id;
self.affliate_Id = affliate_Id;
self.role_id = role_id;
self.username = username;
self.surname = surname;
self.lastname = lastname;
} }
My controller class:
let task = session.dataTask(with: request as URLRequest) { data, response, error in
guard data != nil else {
print("no data found: \(error)")
return
}
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
NSLog("Login SUCCESS");
let prefs:UserDefaults = UserDefaults.standard
prefs.set(username, forKey: "USERNAME")
prefs.set(udid, forKey: "UDID")
prefs.synchronize()
print("Response: \(json)")
//var jsonString = NSString(data: json, encoding: String.Encoding.utf8)! as String
//when I tried to do the above statement, an error is thrown. Cannot convert value of type NSDictionary to expected argument type Data
//var person:UserDetails = UserDetails(json: jsonString)
self.dismiss(animated: true, completion: nil)
} else {
let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)// No error thrown, but not NSDictionary
print("Error could not parse JSON: \(jsonStr)")
}
} catch let parseError {
print(parseError)// Log the error thrown by `JSONObjectWithData`
let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
print("Error could not parse JSON: '\(jsonStr)'")
}
}
task.resume()
JSON Response:
{
"user": {
"token": "ABCDEFGHI",
"agent_id": 0,
"user_id": 151,
"company_id": 1,
"affiliate_Id": 0,
"role_id": 1,
"username": "testman1",
"surname": "Test",
"lastname": "man",
},
"menu": [
{ .....
Can someone help me in solving this. Tia
You should avoid using Foundation classes (NSDictionary etc) and use Swift types.
I also suggest you add a failable initialiser to your UserDetails class that accepts a dictionary:
class UserDetails {
let token: String
let agentId: Int
let userId: Int
let companyId: Int
let affliateId: Int
let roleId: Int
let username: String
let surname: String
let lastname: String
init?(dictionary: [String:Any]) {
guard let token = dictionary["token"] as? String,
let agentId = dictionary["agent_id"] as? Int,
let userId = dictionary["user_id"] as? Int,
... // And so on
else {
return nil
}
self.token = token;
self.agentId = agentId;
self.userId = userId;
self.companyId = companyId;
self.affliateId = affliateId;
self.roleId = roleId;
self.username = username;
self.surname = surname;
self.lastname = lastname;
}
}
and in your completion block:
do {
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String:Any] {
if let userDict = json["user"] as [String:Any] {
guard let userObject = UserDetails(dictionary:userDict) else {
print("Failed to create user from dictionary")
return
}
// Do something with userObject
}
}
} catch let parseError {
I also took the liberty of removing the _ from your properties because _ are icky
First of all you need to use Swift native Dictionary instead of NSDictionary also batter if you define your init method of UserDetails with single parameter of type [String: Any].
class UserDetails {
var token:String = ""
var agent_id: Int = 0
var user_id:Int = 0
var company_id:Int = 0
var affliate_Id:Int = 0
var role_id:Int = 0
var username: String = ""
var surname:String = ""
var lastname:String = ""
init(userDic: [String: Any]) {
if let token = user["token"] as? String, let agent_id = user["agent_id"] as? Int,
let user_id = user["user_id"] as? Int, let company_id = user["company_id"] as? Int,
let affliate_Id = user["affliate_Id"] as? Int, let role_id = user["role_id"] as? Int,
let username = user["username"] as? String, let surname = user["surname"] as? String,
let lastname = user["lastname"] as? String {
self.token = token;
self.agent_id = agent_id;
self.user_id = user_id;
self.company_id = company_id;
self.affliate_Id = affliate_Id;
self.role_id = role_id;
self.username = username;
self.surname = surname;
self.lastname = lastname;
}
}
}
Now simply call this init method from your json response like this.
if let json = try JSONSerialization.jsonObject(with: data!, options: []) as? [String: Any], let userDic = json["user"] as? [String: Any]{
let userDetails = UserDetails(userDic: userDic)
}

Swift: JSON is nil when accessing NSDictionairy

When I print(JSON) I get the files, so the .request works.
But when I am trying to access the "test" key (which exists) I get nil
I get
"I am here"
"now ,I am here"
Alamofire.request(.GET, self.APIBaseUrl , parameters: ["api_key": self.APIkey])
.responseJSON { response in
if let JSON = response.result.value {
print("I am here")
if let str = JSON as? NSDictionary {
print("now , I am here")
if let movieUrlString = str["poster_path"] as? String)! {
print("but now here")
}
EDITED
print(dict)
**dates** = {
maximum = "2015-10-21";
minimum = "2015-09-30";
};
page = 1;
**results** = (
{
adult = 0;
"poster_path" = "/2XegKZ0I4QrvzpEHneigRk6YTB1.jpg";
++(more results)
Try to use more meaningful debug printing statements and variables names. Also you were not using the right variable for subscripting. Fixed example:
Alamofire.request(.GET, self.APIBaseUrl , parameters: ["api_key": self.APIkey]).responseJSON { response in
if let JSON = response.result.value {
print("inside JSON result")
if let dict = JSON as? NSDictionary {
print("inside decoded JSON")
if let results = dict["results"] as? [NSDictionary] {
for result in results {
if let movieUrlString = result["poster_path"] as? String {
print(movieUrlString)
}
}
}
}
}
}