2014-11-08 172 views
30

我正在尋找一種在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

回答

15

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

5

有一個叫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

0

首先,創建一個斯威夫特對象這樣

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

4

你可以做到這一點。它會給你更多的控制變量名稱和值以及準備好的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

20

你可以使用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

0

查看NSKeyValueCoding.h,特別是setValuesForKeysWithDictionary。將json反序列化爲NSDictionary後,您可以使用該字典實例創建並初始化您的對象,無需在該對象上手動設置值。這會給你一個關於反序列化如何與json一起工作的想法,但是你很快就會發現你需要對反序列化過程進行更多的控制。這就是爲什麼我在NSObject上實現了一個類別,它允許在json反序列化過程中完全控制NSObject初始化字典,它基本上比setValuesForKeysWithDictionary能夠做的更加豐富對象。我還有一個由json反序列化器使用的協議,它允許反序列化對象來控制某些方面,例如,如果反序列化一個NSArray對象,它會詢問該對象什麼是存儲在數組中的對象的類型名稱。

4

使用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" 
*/