Автоматическая сериализация JSON и десериализация объектов в Swift
Я ищу способ автоматического сериализации и десериализации экземпляров класса в Swift. Предположим, что мы определили следующий класс...
class Person {
let firstName: String
let lastName: String
init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
}
... и Person
instance:
let person = Person(firstName: "John", lastName: "Doe")
Представление JSON Person
будет следующим:
{
"firstName": "John",
"lastName": "Doe"
}
Теперь вот мои вопросы:
- Как я могу сериализовать экземпляр
Person
и получить вышеупомянутый JSON без необходимости вручную добавлять все свойства класса в словарь, который превращается в JSON?
- Как я могу десериализовать вышеупомянутый JSON и вернуть экземпляр-объект, который статически типизирован как тип
Person
? Опять же, я не хочу отображать свойства вручную.
Вот как вы это сделали бы на С#, используя Json.NET:
var person = new Person("John", "Doe");
string json = JsonConvert.SerializeObject(person);
// {"firstName":"John","lastName":"Doe"}
Person deserializedPerson = JsonConvert.DeserializeObject<Person>(json);
Ответы
Ответ 1
Как указано в 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)
Обратите внимание, что все свойства должны соответствовать протоколу Codable.
Альтернативой может быть JSONCodable, который используется генератором кода Swagger.
Ответ 2
Вы можете использовать EVReflection. Вы можете использовать код, например:
var person:Person = Person(json:jsonString)
или
var jsonString:String = person.toJsonString()
См. страницу GitHub для более подробного примера кода. Вам просто нужно сделать EVObject базовым классом ваших объектов данных. Не требуется сопоставление (если json-ключи совпадают с именами свойств)
Обновление: Swift 4 поддерживает Codable, что делает его почти таким же простым, как EVReflection, но с лучшей производительностью. Если вы хотите использовать простой подрядчик, как описано выше, вы можете использовать это расширение: Stuff/Codable
Ответ 3
Существует класс Foundation под названием NSJSONSerialization
, который может выполнять преобразование в и из JSON
.
Метод преобразования из JSON в объект выглядит следующим образом:
let jsonObject = NSJSONSerialization.JSONObjectWithData(data,
options: NSJSONReadingOptions.MutableContainers,
error: &error) as NSDictionary
Обратите внимание, что первым аргументом этого метода являются данные JSON
, но не как строковый объект, а как объект NSData
(так как вы часто будете получать данные JSON в любом случае).
Скорее всего, вам понадобится метод factory для вашего класса, который принимает данные JSON в качестве аргумента, использует этот метод и возвращает объект инициализации вашего класса.
Чтобы инвертировать этот процесс и создать данные JSON из объекта, вы захотите использовать dataWithJSONObject
, в котором вы передадите объект, который можно преобразовать в JSON, и вернуть NSData?
. Опять же, вы, вероятно, захотите создать вспомогательный метод, который не требует аргументов в качестве метода экземпляра вашего класса.
Насколько я знаю, самый простой способ справиться с этим - создать способ сопоставить свойства объектов в словаре и передать этот словарь для превращения вашего объекта в данные JSON. Затем, когда вы поворачиваете данные JSON в объект, ожидайте, что словарь будет возвращен и изменит процесс сопоставления. Однако может быть проще.
Ответ 4
С Swift 4 вам просто нужно сделать свой класс совместимым с протоколами Codable
(Encodable
и Decodable
), чтобы иметь возможность выполнять сериализацию и десериализацию 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"
*/
Ответ 5
Вы можете достичь этого, используя ObjectMapper. Это даст вам больше контроля над именами переменных и значениями и подготовленным 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)
Ответ 6
Сначала создайте объект Swift, подобный этому
struct Person {
var firstName: String?;
var lastName: String?;
init() {
}
}
После этого выполните сериализацию полученных данных JSON, используя встроенную NSJSONSerialization и проанализируйте значения в объекте Person.
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:
Также обратите внимание на эти библиотеки
Swift-JsonSerialiser
ROJSONParser
Ответ 7
Взгляните на NSKeyValueCoding.h, в частности setValuesForKeysWithDictionary
. После десериализации json в NSDictionary
вы можете создать и инициализировать свой объект с помощью этого экземпляра словаря, не нужно вручную устанавливать значения для объекта. Это даст вам представление о том, как десериализация может работать с json, но вскоре вы узнаете, что вам нужен больше контроля над процессом десериализации. Вот почему я реализую категорию на NSObject
, которая позволяет полностью контролируемую инициализацию NSObject
со словарем во время десериализации json, она в основном обогащает объект даже дальше, чем setValuesForKeysWithDictionary
. У меня также есть протокол, используемый десериализатором json, который позволяет десериализовать объект для управления некоторыми аспектами, например, если десериализация NSArray объектов, он спросит, что объект - это имя типа объектов, хранящихся в массиве.