Deserialize JSON / NSDictionary to Swift objects

JsonSwift

Json Problem Overview


Is there a way to properly deserialize a JSON response to Swift objects resp. using DTOs as containers for fixed JSON APIs?

Something similar to http://james.newtonking.com/json or something like this example from Java

User user = jsonResponse.readEntity(User.class);

whereby jsonResponse.toString() is something like

{
  "name": "myUser", 
  "email": "[email protected]",
  "password": "passwordHash"
}

Json Solutions


Solution 1 - Json

SWIFT 4 Update


Since you give a very simple JSON object the code prepared for to handle that model. If you need more complicated JSON models you need to improve this sample.

Your Custom Object

class Person : NSObject {
    var name : String = ""
    var email : String = ""
    var password : String = ""
    
    init(JSONString: String) {
        super.init()

        var error : NSError?
        let JSONData = JSONString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)

        let JSONDictionary: Dictionary = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &error) as NSDictionary
        
        // Loop
        for (key, value) in JSONDictionary {
            let keyName = key as String
            let keyValue: String = value as String
            
            // If property exists
            if (self.respondsToSelector(NSSelectorFromString(keyName))) {
                self.setValue(keyValue, forKey: keyName)
            }
        }
        // Or you can do it with using 
        // self.setValuesForKeysWithDictionary(JSONDictionary)
        // instead of loop method above
    }
}

And this is how you invoke your custom class with JSON string.

override func viewDidLoad() {
    super.viewDidLoad()
    let jsonString = "{ \"name\":\"myUser\", \"email\":\"[email protected]\", \"password\":\"passwordHash\" }"
    var aPerson : Person = Person(JSONString: jsonString)
    println(aPerson.name) // Output is "myUser"
}

Solution 2 - Json

I recommend that you use code generation (http://www.json4swift.com) to create native models out of the json response, this will save your time of parsing by hand and reduce the risk of errors due to mistaken keys, all elements will be accessible by model properties, this will be purely native and the models will make more sense rather checking the keys.

Your conversion will be as simple as:

let userObject = UserClass(userDictionary)
print(userObject!.name)

Solution 3 - Json

Swift 2: I really like the previous post of Mohacs! To make it more object oriented, i wrote a matching Extension:

extension NSObject{       
    convenience init(jsonStr:String) {            
        self.init()

        if let jsonData = jsonStr.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)
        {
            do {
                let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! [String: AnyObject]
                
                // Loop
                for (key, value) in json {
                    let keyName = key as String
                    let keyValue: String = value as! String
                    
                    // If property exists
                    if (self.respondsToSelector(NSSelectorFromString(keyName))) {
                        self.setValue(keyValue, forKey: keyName)
                    }
                }
                
            } catch let error as NSError {
                print("Failed to load: \(error.localizedDescription)")
            }
        }
        else
        {
            print("json is of wrong format!")
        }
    }
}

custom classes:

class Person : NSObject {
       var name : String?
       var email : String?
       var password : String?
}

class Address : NSObject {
       var city : String?
       var zip : String?
}

invoking custom classes with JSON string:

var jsonString = "{ \"name\":\"myUser\", \"email\":\"[email protected]\", \"password\":\"passwordHash\" }"
let aPerson = Person(jsonStr: jsonString)
print(aPerson.name!) // Output is "myUser"

jsonString = "{ \"city\":\"Berlin\", \"zip\":\"12345\" }"
let aAddress = Address(jsonStr: jsonString)
print(aAddress.city!) // Output is "Berlin"

Solution 4 - Json

Yet another JSON handler I wrote:

With it you can go like this:

let obj:[String:AnyObject] = [    "array": [JSON.null, false, 0, "", [], [:]],
    "object":[        "null":   JSON.null,        "bool":   true,        "int":    42,        "double": 3.141592653589793,        "string": "a α\t弾\n𪚲",        "array":  [],
        "object": [:]
    ],
    "url":"http://blog.livedoor.com/dankogai/"
]

let json = JSON(obj)

json.toString()
json["object"]["null"].asNull       // NSNull()
json["object"]["bool"].asBool       // true
json["object"]["int"].asInt         // 42
json["object"]["double"].asDouble   // 3.141592653589793
json["object"]["string"].asString   // "a α\t弾\n𪚲"
json["array"][0].asNull             // NSNull()
json["array"][1].asBool             // false
json["array"][2].asInt              // 0
json["array"][3].asString           // ""

As you see no !? needed between subscripts.

In addition to that you can apply your own schema like this:

//// schema by subclassing
class MyJSON : JSON {
    override init(_ obj:AnyObject){ super.init(obj) }
    override init(_ json:JSON)  { super.init(json) }
    var null  :NSNull? { return self["null"].asNull }
    var bool  :Bool?   { return self["bool"].asBool }
    var int   :Int?    { return self["int"].asInt }
    var double:Double? { return self["double"].asDouble }
    var string:String? { return self["string"].asString }
    var url:   String? { return self["url"].asString }
    var array :MyJSON  { return MyJSON(self["array"])  }
    var object:MyJSON  { return MyJSON(self["object"]) }
}

let myjson = MyJSON(obj)
myjson.object.null      // NSNull?
myjson.object.bool      // Bool?
myjson.object.int       // Int?
myjson.object.double    // Double?
myjson.object.string    // String?
myjson.url              // String?

Solution 5 - Json

There's a great example by Apple for deserializing JSON with Swift 2.0

The trick is to use the guard keyword and chain the assignments like so:

init?(attributes: [String : AnyObject]) {
    guard let name = attributes["name"] as? String,
        let coordinates = attributes["coordinates"] as? [String: Double],
        let latitude = coordinates["lat"],
        let longitude = coordinates["lng"],
        else {
            return nil
    }
    self.name = name
    self.coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
}

I personally prefer native parsing vs any 3rd party, as it is transparent and magic-less. (and bug less?)

Solution 6 - Json

Using quicktype, I generated your model and serialization helpers from your sample:

import Foundation

struct User: Codable {
    let name: String
    let email: String
    let password: String
}

extension User {
    static func from(json: String, using encoding: String.Encoding = .utf8) -> OtherUser? {
        guard let data = json.data(using: encoding) else { return nil }
        return OtherUser.from(data: data)
    }

    static func from(data: Data) -> OtherUser? {
        let decoder = JSONDecoder()
        return try? decoder.decode(OtherUser.self, from: data)
    }

    var jsonData: Data? {
        let encoder = JSONEncoder()
        return try? encoder.encode(self)
    }

    var jsonString: String? {
        guard let data = self.jsonData else { return nil }
        return String(data: data, encoding: .utf8)
    }
}

Then parse User values like this:

let user = User.from(json: """{
  "name": "myUser", 
  "email": "[email protected]",
  "password": "passwordHash"
}""")!

Solution 7 - Json

I wrote this small open-source library recently that lets you quickly and easily deserialize dictionaries into Swift objects: https://github.com/isair/JSONHelper

Using it, deserializing data becomes as easy as this:

var myInstance = MyClass(data: jsonDictionary)

or

myInstance <-- jsonDictionary

And models need to look only like this:

struct SomeObjectType: Deserializable {
    var someProperty: Int?
    var someOtherProperty: AnotherObjectType?
    var yetAnotherProperty: [YetAnotherObjectType]?

    init(data: [String: AnyObject]) {
        someProperty <-- data["some_key"]
        someOtherProperty <-- data["some_other_key"]
        yetAnotherProperty <-- data["yet_another_key"]
    }
}

Which, in your case, would be:

struct Person: Deserializable {
    var name: String?
    var email: String?
    var password: String?

    init(data: [String: AnyObject]) {
        name <-- data["name"]
        email <-- data["email"]
        password <-- data["password"]
    }
}

Solution 8 - Json

If you would like parse from and to json without the need to manually map keys and fields, then you could also use EVReflection. You can then use code like:

var user:User = User(json:jsonString)

or

var jsonString:String = user.toJsonString()

The only thing you need to do is to use EVObject as your data objects base class. See the GitHub page for more detailed sample code

Solution 9 - Json

HandyJSON is another option to deal with JSON for you. https://github.com/alibaba/handyjson

It deserials JSON to object directly. No need to specify mapping relationship, no need to inherit from NSObject. Just define your pure-swift class/struct, and deserial JSON to it.

class Animal: HandyJSON {
var name: String?
var id: String?
var num: Int?

required init() {}

}

let jsonString = "{"name":"cat","id":"12345","num":180}"

if let animal = JSONDeserializer.deserializeFrom(jsonString) { print(animal) }

Solution 10 - Json

I am expanding upon Mohacs and Peter Kreinz's excellent answers just a bit to cover the array of like objects case where each object contains a mixture of valid JSON data types. If the JSON data one is parsing is an array of like objects containing a mixture of JSON data types, the do loop for parsing the JSON data becomes this.

// Array of parsed objects
var parsedObjects = [ParsedObject]()
do {
    let json = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as [Dictionary<String, AnyObject>]
    // Loop through objects
    for dict in json {
        // ParsedObject is a single instance of an object inside the JSON data
        // Its properties are a mixture of String, Int, Double and Bool
        let parsedObject = ParsedObject()
        // Loop through key/values in object parsed from JSON
        for (key, value) in json {
            // If property exists, set the value
            if (parsedObject.respondsToSelector(NSSelectorFromString(keyName))) {
                // setValue can handle AnyObject when assigning property value
                parsedObject.setValue(keyValue, forKey: keyName)
            }
        }
        parsedObjects.append(parsedObject)
    }
} catch let error as NSError {
    print("Failed to load: \(error.localizedDescription)")
}

Solution 11 - Json

This way lets you get the user from a URL. It's parse the NSData to a NSDictionary and then to your NSObject.

let urlS = "http://api.localhost:3000/"

func getUser(username: Strung) -> User {
   var user = User()
   let url = NSURL(string: "\(urlS)\(username)")
   if let data = NSData(contentsOfURL: url!) {
     setKeysAndValues(user, dictionary: parseData(data))
   }
   return user
}

func setKeysAndValues (object : AnyObject, dictionary : NSDictionary)  -> AnyObject  {
    for (key, value) in dictionary {
        if let key = key  as? String, let value = value as? String {
            if (object.respondsToSelector(NSSelectorFromString(key))) {
                object.setValue(value, forKey: key)
            }
        }
    }
    return object
}

func parseData (data : NSData)  -> NSDictionary  {
    var error: NSError?
    return NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as! NSDictionary
}

Solution 12 - Json

In Swift 4, You can use the Decoding, CodingKey protocols to deserialize the JSON response:

  1. Create the class which confirm the decodable protocol

class UserInfo: Decodable

  1. Create members of the class

var name: String

var email: String

var password: String

  1. Create JSON key enum which inherits from CodingKey

enum UserInfoCodingKey: String, CodingKey { case name case password case emailId } 4. Implement init

required init(from decoder: Decoder) throws

The whole class look like :

enter image description here

  1. Call Decoder

// jsonData is JSON response and we get the userInfo object

let userInfo = try JsonDecoder().decode(UserInfo.self, from: jsonData)

Solution 13 - Json

You do this by using NSJSONSerialization. Where data is your JSON.

First wrap it in an if statement to provide some error handling capablity

if let data = data,
 json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: AnyObject] {
// Do stuff
} else {
  // Do stuff
  print("No Data :/")
}

then assign them:

let email = json["email"] as? String
let name = json["name"] as? String
let password = json["password"] as? String

Now, This will show you the result:

print("Found User iname: \(name) with email: \(email) and pass \(password)")

Taken from this Swift Parse JSON tutorial. You should check out the tutorial as it goes a lot more in depth and covers better error handling.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondimitriView Question on Stackoverflow
Solution 1 - JsonmodusView Answer on Stackoverflow
Solution 2 - JsonSyed AbsarView Answer on Stackoverflow
Solution 3 - JsonPeter KreinzView Answer on Stackoverflow
Solution 4 - JsondankogaiView Answer on Stackoverflow
Solution 5 - JsonAmitPView Answer on Stackoverflow
Solution 6 - JsonDavid SiegelView Answer on Stackoverflow
Solution 7 - JsonisairView Answer on Stackoverflow
Solution 8 - JsonEdwin VermeerView Answer on Stackoverflow
Solution 9 - JsonxuyecanView Answer on Stackoverflow
Solution 10 - Jsonjkwuc89View Answer on Stackoverflow
Solution 11 - JsonEduardo IriasView Answer on Stackoverflow
Solution 12 - Jsonyo2bhView Answer on Stackoverflow
Solution 13 - JsonMarkPView Answer on Stackoverflow