Deserialize JSON / NSDictionary to Swift objects

前端 未结 13 1502
情歌与酒
情歌与酒 2020-11-29 18:43

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.newtonk

相关标签:
13条回答
  • 2020-11-29 18:49

    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\":\"user@example.com\", \"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"
    
    0 讨论(0)
  • 2020-11-29 18:51

    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

    2. Create members of the class

      var name: String

      var email: String

      var password: String

    3. 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 :

    5. Call Decoder

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

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

    0 讨论(0)
  • 2020-11-29 18:52

    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)")
    }
    
    0 讨论(0)
  • 2020-11-29 18:57

    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": "user@example.com",
      "password": "passwordHash"
    }""")!
    
    0 讨论(0)
  • 2020-11-29 18:57

    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)
    }
    
    0 讨论(0)
  • 2020-11-29 18:58

    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.

    0 讨论(0)
提交回复
热议问题