Подтвердить что ты не робот

Автоматическая сериализация 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);
4b9b3361

Ответ 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 объектов, он спросит, что объект - это имя типа объектов, хранящихся в массиве.