在Swift中自动JSON序列化和反序列化对象

问题描述:

我正在寻找一种在Swift中自动序列化和反序列化类实例的方法。假设我们已经定义了以下类...在Swift中自动JSON序列化和反序列化对象

class Person { 
    let firstName: String 
    let lastName: String 

    init(firstName: String, lastName: String) { 
     self.firstName = firstName 
     self.lastName = lastName 
    } 
} 

...和Person例如:

let person = Person(firstName: "John", lastName: "Doe") 

person的JSON表示是以下几点:

{ 
    "firstName": "John", 
    "lastName": "Doe" 
} 

现在,这里是我的问题:

  1. 如何序列化person实例并获取上述JSON而无需手动将该类的所有属性添加到转换为JSON的字典中?
  2. 我该如何反序列化上面的JSON并获取静态类型为Person类型的实例化对象?同样,我不想手动映射属性。

这里是你如何在C#中使用Json.NET做:

var person = new Person("John", "Doe"); 
string json = JsonConvert.SerializeObject(person); 
// {"firstName":"John","lastName":"Doe"} 

Person deserializedPerson = JsonConvert.DeserializeObject<Person>(json); 
+0

通常在Swift/Objective-C中,序列化将意味着转换为'NSData',因此可以存储对象作为数据而不是一些纯文本格式。 – nhgrif 2014-11-08 18:46:45

+0

这里不需要第三方Json.NET,DataContractJsonSerializer可以做到这一点。 – 2016-04-07 11:35:18

+0

@Agent_L我不想在这里深入讨论这个问题,但是Newtonsoft.Json比'DataContractJsonSerializer'更好用,功能也更丰富。 – 2016-04-07 11:37:48

WWDC2017 @ 24:48(Swift 4)所示,我们将能够使用Codable协议。实施例

public struct Person : Codable { 
    public let firstName:String 
    public let lastName:String 
    public let location:Location 
} 

然后

let payload: Data = try JSONEncoder().encode(person) 

反序列化

let anotherPerson = try JSONDecoder().decode(Person.self, from: payload) 

所有属性必须符合可编码协议。

一个替代,也可以是JSONCodable这是Swagger's它使用的代码生成器。

+0

是否有可能像字典一样属性作为结构的一部分,仍然使用Codable?例如。 var items:[String:Any]? 。在这种情况下可以编码工作?我觉得,它不会。 – lionserdar 2017-06-09 14:52:59

+0

您是否知道'Codable'基础设施是否可以重复用于其他编码。例如,按照协议缓冲器的样式进行二进制编码。 – Benjohn 2017-09-08 14:19:44

+0

这很好,但你如何解码多个项目?在我的有效载荷包含Person.self数组的情况下? – chrisl08 2017-10-04 07:13:03

有一个叫NSJSONSerialization基金会类可以做转换和从JSON

从JSON转换为对象的方法,看起来像这样:

let jsonObject = NSJSONSerialization.JSONObjectWithData(data, 
    options: NSJSONReadingOptions.MutableContainers, 
    error: &error) as NSDictionary 

。注意,第一个参数这个方法是JSON数据,但不作为一个字符串对象,而不是作为一个NSData对象(这就是你通常会多次获取JSON数据的方式)。

您很可能会希望为您的类使用JSON数据作为参数的工厂方法,使用此方法并返回类的初始化对象。

要反转此过程并从对象中创建JSON数据,您需要使用dataWithJSONObject,其中您将传递一个可以转换为JSON并返回NSData?的对象。同样,你可能想要创建一个不需要参数作为你的类的实例方法的帮助器方法。


据我所知,来处理这个最简单的方法是创建一个方法来你的对象的属性映射到一个字典,并传递字典把你的对象变成JSON数据。然后,当您将JSON数据转换为对象时,需要返回一个字典并反转映射过程。虽然可能有一个更简单的方法。

+1

我知道'NSJSONSerialization'和使用字典,但这是特别*不*我要找的。手动完成所有这些都是编写样板代码的一种容易出错的方式,我试图在这里避免。 – 2014-11-08 19:05:43

+2

您可以使用'NSCoding',但它仍然需要一些样板代码。请参阅:http://nshipster.com/nscoding/ – DPlusV 2014-11-08 19:16:25

首先,创建一个斯威夫特对象这样

struct Person { 
    var firstName: String?; 
    var lastName: String?; 
    init() { 

    } 
} 

之后,系列化你的JSON数据您检索,使用内置NSJSONSerialization和解析值到目的。

var person = Person(); 
var error: NSError?; 
var response: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions(), error: &error); 

if let personDictionary = response as? NSDictionary { 
    person.firstName = personDictionary["firstName"] as? String; 
    person.lastName = personDictionary["lastName"] as? String; 
} 

UPDATE:

也请使用ObjectMapper图书馆看看那些库

Swift-JsonSerialiser

ROJSONParser

+1

这种方法很有效,但恐怕不是我的问题的答案。我正在寻找**不需要手动设置每个属性的解决方案。 – 2015-03-01 11:14:28

+1

@MariusSchulz我认为回答“没有这样的第一方东西”也是一个重要的答案,虽然在这里可以更清楚。 – 2016-04-07 11:31:48

你可以做到这一点。它会给你更多的控制变量名称和值以及准备好的JSON。添加该库后,扩展Mappable类并定义mapping(map: Map)函数。

例如

class User: Mappable { 
     var id: Int? 
     var name: String? 

     required init?(_ map: Map) { 

     } 

     // Mapping code 
     func mapping(map: Map) { 
      name <- map["name"] 
      id  <- map["id"] 
     } 

    } 

使用它像下面

let user = Mapper<User>().map(JSONString) 
+1

所有这些都是为OP所要避免的样板代码添加一个很好的语法:为每个属性编写一串序列化步骤。 – 2016-12-01 15:03:02

+0

同意。这个答案好得多http://stackoverflow.com/a/30875255/2538729 – 2016-12-01 15:10:31

你可以使用EVReflection了点。您可以使用如下代码:

var person:Person = Person(json:jsonString) 

var jsonString:String = person.toJsonString() 

查看更详细的示例代码GitHub页面您只需将EVObject作为数据对象的基类即可。无需映射(只要json键与属性名称相同)

更新: Swift 4支持Codable,使其与EVReflection几乎一样容易,但性能更佳。如果您确实想使用上述简单的承包商,那么您可以使用此扩展名:Stuff/Codable

+1

伟大的,这就是我一直在寻找。来自C#和Ruby,解析JSON是在iOS开发中的地狱。 – Peterdk 2017-03-11 20:24:35

+1

EV反射是无用的。非常感谢你 – 2017-09-07 11:17:12

查看NSKeyValueCoding.h,特别是setValuesForKeysWithDictionary。将json反序列化为NSDictionary后,您可以使用该字典实例创建并初始化您的对象,无需在该对象上手动设置值。这会给你一个关于反序列化如何与json一起工作的想法,但是你很快就会发现你需要对反序列化过程进行更多的控制。这就是为什么我在NSObject上实现了一个类别,它允许在json反序列化过程中完全控制NSObject初始化字典,它基本上比setValuesForKeysWithDictionary能够做的更加丰富对象。我还有一个由json反序列化器使用的协议,它允许反序列化对象来控制某些方面,例如,如果反序列化一个NSArray对象,它会询问该对象什么是存储在数组中的对象的类型名称。

使用Swift 4,您只需使类符合CodableEncodableDecodable协议)以便能够执行JSON序列化和反序列化。

import Foundation 

class Person: Codable { 
    let firstName: String 
    let lastName: String 

    init(firstName: String, lastName: String) { 
     self.firstName = firstName 
     self.lastName = lastName 
    } 
} 

用法#1(Person实例编码成JSON字符串):

let person = Person(firstName: "John", lastName: "Doe") 
let encoder = JSONEncoder() 
encoder.outputFormatting = .prettyPrinted // if necessary 
let data = try! encoder.encode(person) 
let jsonString = String(data: data, encoding: .utf8)! 
print(jsonString) 

/* 
prints: 
{ 
    "firstName" : "John", 
    "lastName" : "Doe" 
} 
*/ 

用法#2(JSON字符串解码成Person实例):

let jsonString = """ 
{ 
    "firstName" : "John", 
    "lastName" : "Doe" 
} 
""" 

let jsonData = jsonString.data(using: .utf8)! 
let decoder = JSONDecoder() 
let person = try! decoder.decode(Person.self, from: jsonData) 
dump(person) 

/* 
prints: 
▿ __lldb_expr_609.Person #0 
    - firstName: "John" 
    - lastName: "Doe" 
*/