将JSON / NSDictionary反序列化为Swift对象

时间:2014-06-19 15:24:20

标签: json swift

有没有办法正确反序列化对Swift对象的JSON响应。使用DTO作为固定JSON API的容器?

类似于http://james.newtonking.com/json的东西或类似Java中的这个例子

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

jsonResponse.toString()就像

{
  "name": "myUser", 
  "email": "user@example.com",
  "password": "passwordHash"
}

13 个答案:

答案 0 :(得分:58)

SWIFT 4 Update


因为您为一个非常简单的JSON对象提供了为处理该模型而准备的代码。如果您需要更复杂的JSON模型,则需要改进此示例。

您的自定义对象

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

这就是您使用JSON字符串调用自定义类的方法。

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

答案 1 :(得分:11)

我建议您使用代码生成(http://www.json4swift.com)从json响应中创建本机模型,这将节省您手动解析的时间并降低因错误键导致错误的风险,所有元素都将可以通过模型属性访问,这将是纯粹原生的,模型将更有意义,而不是检查键。

您的转化将非常简单:

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

答案 2 :(得分:9)

Swift 2 :我非常喜欢上一篇Mohacs的帖子!为了使它更面向对象,我写了一个匹配的扩展名:

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

自定义类:

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

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

使用JSON字符串调用自定义类:

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"

答案 3 :(得分:7)

我写的另一个JSON处理程序:

有了它,你可以这样:

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

如下所示,下标之间不需要!?

除此之外,您还可以应用自己的架构:

//// 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?

答案 4 :(得分:4)

Apple有great example用于使用Swift 2.0反序列化JSON

诀窍是使用guard关键字并链接分配,如下所示:

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

我个人更喜欢原生解析与任何第三方,因为它透明且无魔法。 (并减少错误?)

答案 5 :(得分:3)

我最近编写了这个小型开源库,可以让您快速轻松地将字典反序列化为Swift对象:https://github.com/isair/JSONHelper

使用它,反序列化数据变得如此简单:

var myInstance = MyClass(data: jsonDictionary)

myInstance <-- jsonDictionary

模特需要看起来像这样:

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

在您的情况下,将是:

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

答案 6 :(得分:3)

如果您想要从json解析并且不需要手动映射键和字段,那么您也可以使用EVReflection。然后,您可以使用以下代码:

var user:User = User(json:jsonString)

var jsonString:String = user.toJsonString()

您唯一需要做的就是使用EVObject作为数据对象基类。 有关更详细的示例代码,请参阅GitHub页面

答案 7 :(得分:3)

HandyJSON 是另一种为您处理JSON的选项。 https://github.com/alibaba/handyjson

将JSON直接解析为对象。无需指定映射关系,无需从NSObject继承。只需定义你的pure-swift类/结构,并将JSON反序列化即可。

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

答案 8 :(得分:3)

使用quicktype,我从您的示例中生成了模型和序列化助手:

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

然后解析User这样的值:

let user = User.from(json: """{
  "name": "myUser", 
  "email": "user@example.com",
  "password": "passwordHash"
}""")!

答案 9 :(得分:2)

我正在扩展Mohacs和Peter Kreinz的优秀答案,以覆盖类似对象的数组,其中每个对象包含有效JSON数据类型的混合。如果正在解析的JSON数据是包含JSON数据类型混合的类似对象的数组,则用于解析JSON数据的do循环变为此。

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

答案 10 :(得分:2)

在Swift 4中,您可以使用解码 CodingKey协议来反序列化JSON响应:

  1. 创建确认可解码协议的类

    class UserInfo: Decodable

  2. 创建班级成员

    var name: String

    var email: String

    var password: String

  3. 创建继承自CodingKey的JSON密钥枚举

    enum UserInfoCodingKey: String, CodingKey { case name case password case emailId }

  4. 实施init

    required init(from decoder: Decoder) throws

    全班看起来像:

    enter image description here

  5. 呼叫解码器

    // jsonData是JSON响应,我们得到userInfo对象

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

答案 11 :(得分:1)

这种方式可让您从URL获取用户。它将NSData解析为NSDictionary,然后解析为您的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
}

答案 12 :(得分:0)

您可以使用 NSJSONSerialization 来完成此操作。其中数据是您的JSON。

首先将其包装在if语句中以提供一些错误处理功能

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

然后分配它们:

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

现在,这将显示结果:

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

取自此Swift Parse JSON教程。您应该查看教程,因为它更深入,并涵盖更好的错误处理。