How to parsing Json response to Swift objects - json

Hi i am beginner for swift language and in my project i am using web services and after got response how can i parse below response to Swift object can some on help me please
response:-
[
{
"id" : 1,
"first_name": "John",
"last_name": "Smith",
"age": 25,
"address": {
"id": 1,
"street_address": "2nd Street",
"city": "Bakersfield",
"state": "CA",
"postal_code": 93309
}
}
]
ModelClass:-
class Address:NSObject{
struct Address {
let objID: Int?
let streetAddress: String?
let city: String?
let state: String?
let postalCode: String?
}
struct User {
let objID: Int?
let firstName: String?
let lastName: String?
let age: Int?
let address : Address?
}
}
ViewController:-
func finalResponse(response : AnyObject){
let addressArray = response as! NSArray;
for items in addressArray{
}
}

In swift 4 it get lot easier
Your Model class look like this
Key should be same as json response or make enum for changing the name
struct Address: Decodable {
let objID: Int?
let streetAddress: String?
let city: String?
let state: String?
let postalCode: String?
}
struct User: Decodable {
let objID: Int?
let firstName: String?
let lastName: String?
let age: Int?
let address : Address?
}
}
Your view Controller class look like this
try decoder.decode([User.self], from: jsonData)

This is olden days method in objective-c swift 1,2,3
This is model Class
class SafeJson: NSObject{
override func setValue(_ value: Any?, forKey key: String) {
let firstCharacter = String(key.characters.first!).uppercased()
let range = NSMakeRange(0,1)
let valuex = NSString(string: key).replacingCharacters(in: range, with: firstCharacter)
// let valuex = key.replacingCharacters(in: range, offsetBy: 0), with: firstCharacter)
let selector = NSSelectorFromString("set\(valuex):")
let respond = self.responds(to: selector)
if !respond{
return
}
super.setValue(value, forKey: key)
}
}
class Model:SafeJson{
// var thumbnail_image_name: String?
var title: String?
var number_of_views: NSNumber?
var channel: Channel?
override func setValue(_ value: Any?, forKey key: String) {
if key == "channel"{
self.channel = Channel()
let dictionary = value as! [String: AnyObject]
self.channel?.setValuesForKeys(dictionary)
}else{
super.setValue(value, forKey: key)
}
}
init(dictionary: [String: AnyObject]) {
super.init()
setValuesForKeys(dictionary)
}
}
class Channel:SafeJson{
var name: String?
var profile_image_name: String?
}
In your View controller class You have to pass your response to Model.init it automatically save to model
But in swift 4 setValueForKey is depricated
You have to use decodable for it

warning, did not get to test it so let me know for any warnings and will adjust
For Swift3
func finalResponse(response : AnyObject){
var result: [Address] = []
let json = response as! [String:AnyObject]
// ITERATE THROUGH THE ARRAY OF DICTIONARIES
for item in json {
// a WILL BE THE MAIN OBJECT YOU'RE CREATING
var a = Adress()
a.objID = item["id"] as! Int
.....
// REPEAT FOR EVERY ELEMENT UNTIL YOU REACH NEXT SUBCLASS
.....
// CREATE A NEW DICTIONARY FOR THE SUBCLASS Address
var b = item["address"] as! Dictionary<String, String>
a.address.objID = b["id"] as! Int
// IF YOU DON'T WANT TO CREATE var b YOU CAN WRITE
// a.address.objID = a["address"]["id"] INSTEAD
// ADD YOUR OBJECT TO RESULT
result.append(a)
}
}

Herr is the code to parse data from your JSON data. Create struct at the location that I commented in the code.
do{
let json = try JSONSerialization.jsonObject(with: yourJSONData!, options: []) as? [Any]
let firstUser = json?[0] as? [String: Any]
let id = firstUser?["id"] as? Int
let firstName = firstUser?["first_name"] as? String
//etc... for other keys
let address = firstUser?["address"] as? [String, Any]
let streetAddress = address?["street_address"] as? String
let state = address?["state"] as? String
//etc... create your address struct here and then create the user struct
dump(firstPerson?["lastName"])
}catch let error{
}

Related

Generic decoding json into object with optionals

I wrote an extension to Decodable with the hopes of having a generic constructor for objects from json strings, it looks like this:
extension Decodable {
init?(with dictionary: [String: Any]) {
guard let data = try? JSONSerialization.data(
withJSONObject: dictionary,
options: .prettyPrinted
) else {
return nil
}
guard let result = try? JSONDecoder().decode(
Self.self,
from: data
) else {
return nil
}
self = result
}
}
An example use case looks like this:
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else { return }
guard var goer = Goer(with: json) else { return }
And the object I'm trying to decode into looks like this:
struct Goer: Codable {
let goerId: String
let created: Double
let username: String
let firstName: String
let lastName: String
let city: String
let bio: String
let isPrivate: Bool
let numFollowers: Int
let numFollowing: Int
let followers: [GoerFollow]
let following: [GoerFollow]
}
My issue is that I want to introduce some optionals to these objects that the json strings I'm trying to decode may or may not have keys for. This generic constructor fails in the case where there is no key:value in the json for an optional variable in the object.
I've seen that I can write a custom constructor with a decoder for each object and use the decodeIfPresent function but I wonder if there is a way to do it generically.
if let jsonData = data {
do {
var model = try decoder.decode(Goer.self, from: jsonData)
print("model:\(model)")
} catch {
print("error:\(error)")
}
}
struct Goer: Codable {
let goerId: String?
let created: Double?
let username: String?
let firstName: String?
let lastName: String?
let city: String?
let bio: String?
let isPrivate: Bool?
let numFollowers: Int?
let numFollowing: Int?
let followers: [GoerFollow]?
let following: [GoerFollow]?
}

Deserialization JSON swift 4.2

I try to deserialize my JSON by using Decodable protocol, also i use enum with CodingKey, but it doesn't work. I need only nested array (start with "indicator"), and only few fields (all of them in struct). I tried a lot of different options, but unfortunately..
P.S. Also i tried to do it without CodingKey. Anyway response was: "Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "country", intValue: nil)" Ofc i read this, maybe array is a reason(i mean this strange intValue)?
JSON
[
{
"page":1,
"pages":2,
"per_page":50,
"total":59,
"sourceid":"2",
"lastupdated":"2019-03-21"
},
[
{
"indicator":{
"id":"IP.PAT.RESD",
"value":"Patent applications, residents"
},
"country":{
"id":"SS",
"value":"South Sudan"
},
"countryiso3code":"SSD",
"date":"2018",
"value":null,
"unit":"",
"obs_status":"",
"decimal":0
},
{
"indicator":{
"id":"IP.PAT.RESD",
"value":"Patent applications, residents"
},
"country":{
"id":"SS",
"value":"South Sudan"
},
"countryiso3code":"SSD",
"date":"2017",
"value":null,
"unit":"",
"obs_status":"",
"decimal":0
},
...
]
]
My code
struct CountryObject: Decodable{
var country: CountryInfo
var date: Int
var value: Int?
private enum RawValues: String, Decodable{
case date = "date"
case vallue = "value"
}
}
struct CountryInfo: Decodable{//Country names
var id: String?
var value: String?
private enum RawValues: String, Decodable{
case id = "id"
case value = "value"
}
}//
let urlString = "https://api.worldbank.org/v2/country/SS/indicator/IP.PAT.RESD?format=json"
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTask(with: url) {(data,response,error) in
guard let data = data else {return}
guard error == nil else {return}
do{
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let countryObject = try! decoder.decode([CountryObject].self, from: data)
print(countryObject)
}catch let error{
print(error)
}
}.resume()
Create a root struct and decode the array with unkeyedContainer
struct Root : Decodable {
let info : Info
let countryObjects : [CountryObject]
init(from decoder: Decoder) throws {
var arrayContrainer = try decoder.unkeyedContainer()
info = try arrayContrainer.decode(Info.self)
countryObject = try arrayContrainer.decode([CountryObject].self)
}
}
struct Info : Decodable {
let page, pages, perPage: Int
let lastupdated: String
}
struct CountryObject : Decodable {
let country: CountryInfo
let date: String
let value: Int?
}
struct CountryInfo : Decodable { //Country names
let id: String
let value: String
}
...
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
let root = try decoder.decode(Root.self, from: data)
let countryObjects = root.countryObjects
print(countryObjects)
} catch { print(error) }
(De)serializing the JSON twice is unnecessarily expensive.

Using JSON Decodable in Swift 4.1

{
"count":30,
"recipes":[
{
"publisher":"Closet Cooking",
"f2f_url":"http://food2fork.com/view/35382",
"title":"Jalapeno Popper Grilled Cheese Sandwich",
"source_url":"http://www.closetcooking.com/2011/04/jalapeno-popper-grilled-cheese-sandwich.html",
"recipe_id":"35382",
"image_url":"http://static.food2fork.com/Jalapeno2BPopper2BGrilled2BCheese2BSandwich2B12B500fd186186.jpg",
"social_rank":100.0,
"publisher_url":"http://closetcooking.com"
}
]
}
How can I parse this JSON using Swift 4.1 Decodable please?
Your previous question is quite close but you have to add the struct for the root object
Declare the struct members non-optional as much as possible. The URLs can be decoded as URL
struct Root : Decodable {
let count : Int
let recipes : [Recipe]
}
struct Recipe : Decodable { // It's highly recommended to declare Recipe in singular form
let recipeId : String
let imageUrl, sourceUrl, f2fUrl : URL
let title : String
let publisher : String
let socialRank : Double
let page : Int?
let ingredients : [String]?
}
Now decode
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Root.self, from: data)
self.recipes = result.recipes
below are the models for your JSON:
struct Recipe: Codable{
let publisher: String
let f2f_url: String
let title: String
let source_url: String
let recipe_id: String
let image_url: String
let social_rank: Float
let publisher_url: String
}
struct Model: Codable {
let count: Int
let recipes: [Recipe]
}
and below is JSON decodable:
let json = """
{
"count":30,
"recipes":[
{
"publisher":"Closet Cooking",
"f2f_url":"http://food2fork.com/view/35382",
"title":"Jalapeno Popper Grilled Cheese Sandwich",
"source_url":"http://www.closetcooking.com/2011/04/jalapeno-popper-grilled-cheese-sandwich.html",
"recipe_id":"35382",
"image_url":"http://static.food2fork.com/Jalapeno2BPopper2BGrilled2BCheese2BSandwich2B12B500fd186186.jpg",
"social_rank":100.0,
"publisher_url":"http://closetcooking.com"
}
]
}
""".data(using: .utf8)!
let decoder = JSONDecoder()
do {
let model = try decoder.decode(Model.self, from: json) //Decode JSON Response Data
print(model)
} catch let parsingError {
print("Error", parsingError)
}

Swift, How to Parse/Decode the JSON using Decodable and Codable, When key are unknow/dynamic

Below is my JSON, and I am not able to decode(using CodingKeys)
The data within the regions key is a Dictionary ("IN-WB", "IN-DL" & so on....), as the keys are dynamic, it can be changed more or less.
Please help me parsing the same using Decodable and Codable.
All the data should be within the single model.
{
"provider_code": "AIIN",
"name": "Jio India",
"regions": [
{
"IN-WB": "West Bengal"
},
{
"IN-DL": "Delhi NCR"
},
{
"IN-TN": "Tamil Nadu"
},
{
"IN": "India"
}
]
}
Just use a Dictionary for the regions.
struct Locations: Codable {
let providerCode: String
let name: String
let regions: [[String: String]]
enum CodingKeys: String, CodingKey {
case providerCode = "provider_code"
case name, regions
}
}
You cannot create a specific model for the regions as you wont know the property names
One of possible approach, without using dictionary. But still we have to found key at first )
I like this style as we can use Regions from beginning.
// example data.
let string = "{\"provider_code\":\"AIIN\",\"name\":\"Jio India\",\"regions\":[{\"IN-WB\":\"West Bengal\"},{\"IN-DL\":\"Delhi NCR\"},{\"IN-TN\":\"Tamil Nadu\"},{\"IN\":\"India\"}]}"
let data = string.data(using: .utf8)!
// little helper
struct DynamicGlobalKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
// model
struct Location: Decodable {
let providerCode: String
let name: String
let regions: [Region]
}
extension Location {
struct Region: Decodable {
let key: String
let name: String
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DynamicGlobalKey.self)
key = container.allKeys.first!.stringValue
name = try container.decode(String.self, forKey: container.allKeys.first!)
}
}
}
// example of decoding.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let location = try decoder.decode(Location.self, from: data)

How to decode this using JSONDecoder and Swift 4 Decodabale class

I have decoded this using JSONSerialization. But for my own knowledge and the maintenance of my code. I would like to know how to decode this.
This is what I have so far:
let urlString = "site deleted" + "/DataSource/Mobile/?Action=MyProfile&uid=" + uid + "&uuid=" + uuid
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTask(with: url) { (data, _, error) in
if let err = error {
print("Handle MyProfileJSON error: ", err)
}
guard let data = data else {return}
do {
// swift 4.2 but we cant use it right now
let profile = try JSONDecoder().decode(RequestResult.self, from: data)
print(profile)
completion(profile)
} catch let err {
print("Handle Decoder Error: ", err)
}
}.resume()
I'm not too worried about the cases but this is what I have so far. I know the case I use is not the convention, that's why I did this with JSONSerialization so I can use camelCase. If you can help me convert it to camelCase too that would be amazing but my focus is to Decode this using Decodable class. Thanks a lot, guys.
And this are my structs:
struct RequestResult: Decodable {
var Action: String?
var Params: [String: String]?
var DATA: [String: String]?
}
struct Params: Decodable {
var Action_get: String?
var uid_get: String?
}
struct DATA: Decodable {
var Id: String?
var UserCode: String?
var HomePhone: String?
var Mobile: String?
var WorkPhone: String?
var Email: String?
var AltEmail: String?
var UnitNo: String?
var StreetNo: String?
var StreetName: String?
var City: String?
var StateProvince: String?
var Country: String?
var ZipPostalCode: String?
}
The structure of the JSON is very clear
The root object RequestResult contains a string and two dictionaries.
The dictionaries are replaced by structs.
The CodingKeys are useful to rename the keys to more meaningful and naming convention conforming names. The left side of an enum case is the struct member name, the right side the original JSON key.
A struct member name must match the dictionary key (or the mapped CodingKey).
The struct names are arbitrary.
All struct members can be declared as constants (let) and as non-optional if the JSON contain always the keys.
struct RequestResult: Decodable {
private enum CodingKeys : String, CodingKey {
case action = "Action", params = "Params", data = "DATA"
}
let action: String
let params: Parameter
let data: UserData
}
The dictionary for key Params will be renamed to Parameter and DATA to UserData
struct Parameter: Decodable {
private enum CodingKeys : String, CodingKey {
case action = "Action_get", uid = "uid_get"
}
let action: String
let get: String
}
struct UserData: Decodable {
private enum CodingKeys : String, CodingKey {
case id = "Id", userCode = "UserCode", homePhone = "HomePhone"
case mobile = "Mobile", workPhone = "WorkPhone", email = "Email"
case altEmail = "AltEmail", unitNo = "UnitNo", streetNo = "StreetNo"
case streetName = "StreetName", city = "City", stateProvince = "StateProvince"
case country = "Country", zipPostalCode = "ZipPostalCode"
}
let id: String, userCode, homePhone, mobile: String
let workPhone, email, altEmail, unitNo: String
let streetNo, streetName, city, stateProvince: String
let country, zipPostalCode: String
}