Расшифруйте пользовательский JSON с помощью Decodable + Realm Swift - PullRequest
0 голосов
/ 22 ноября 2018

С сервера мне возвращается большой JSON, который выглядит примерно так:

{
  "id": "123",
  "status": "ok",
  "person": {
    "administration": {
      "name": "John"
    }
  },
  "company": {
    "name": "Test"
  }
}

У меня есть структура:

struct Info: Decodable, Object {
  let id: String
  let status: String
  let personName: String
  let companyName: String
}

Он соответствует декодируемому протоколу и также являетсяОбъект (сущность Царства).Мой вопрос: могу ли я как-то расшифровать имя человека в personName?Что-то вроде person.administration.name.
Я хочу, чтобы конечный объект области был плоским, и в основном все поля являются строками.
Должен ли я создавать отдельные структуры для Person / Company, не являясь объектами области и вметод декодирования, чтобы установить соответствующее значение "personName"?

let personName: String = try container.decode((Person.Administration.name).self, forKey: .personName)

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Рекомендуется разделять транспортные типы, в которые вы анализируете свой JSON, и типы для представления объекта в хранилище.

Но если вы хотите использовать эти комбинированные типы, вам следует сделать что-то вроде этого:

struct Info: Decodable {
    let id: String
    let status: String
    let personName: String
    let companyName: String

    // JSON root keys
    private enum RootKeys: String, CodingKey {
        case id, status, person, company
    }
    // Keys for "person" nested "object"
    private enum PersonKeys: String, CodingKey {
        case administration
    }
    // Keys for "administration" and "company"
    private enum NamedKeys: String, CodingKey {
        case name
    }

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: RootKeys.self)

        self.id = try container.decode(String.self, forKey: .id)
        self.status = try container.decode(String.self, forKey: .status)

        let personContainer = try container.nestedContainer(keyedBy: PersonKeys.self, forKey: .person)
        let administrationContainer = try personContainer.nestedContainer(keyedBy: NamedKeys.self, forKey: .administration)
        self.personName = try administrationContainer.decode(String.self, forKey: .name)

        let companyContainer = try container.nestedContainer(keyedBy: NamedKeys.self, forKey: .company)
        self.companyName = try companyContainer.decode(String.self, forKey: .name)
    }
}

Я разделил ключи на три разных типа CodingKey для обеспечения безопасности некоторых типов и предотвращения случайного смешения.

0 голосов
/ 22 ноября 2018

Вы можете просто использовать containers для декодирования вложенных данных с помощью Decodable, т.е.

struct Info: Decodable {
    let id: String
    let status: String
    let personName: String
    let companyName: String

    enum CodingKeys: String, CodingKey {
        case id, status
        case person, administration
        case company
        case name
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        id = try values.decode(String.self, forKey: .id)
        status = try values.decode(String.self, forKey: .status)

        //Decoding personName
        let person = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
        let administration = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .administration)
        personName = try administration.decode(String.self, forKey: .name)

        //Decoding companyName
        let company = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
        companyName = try company.decode(String.self, forKey: .name)
    }
}

Пример:

IВы расшифровали JSON, который вы указали выше, то есть

if let data = json.data(using: .utf8) {
    let info = try? JSONDecoder().decode(Info.self, from: data)
    print(info)
}

Выходные данные:

(id: "123", status: "ok", personName: "John", companyName: "Test")

Вы можетевыделите CodingKeys для всех различных уровней по вашему желанию.Я сохранил их на одном уровне для простоты.

Предложение: Попробуйте использовать optional types с Codable.Это связано с тем, что ответ API может быть неожиданным.И если вы не получите никакой ожидаемой пары ключ-значение, вы можете получить nil при создании объекта.

...