Словарь декодирования как массив в Swift 4

Моя структура данных выглядит так. «люди» — это словарь ключей, значениями которых является словарь человека:

  "humans" : {
    "abc123" : {
      "name" : "Vince",
      "pets" : [ {
        "animal" : "dog",
        "name" : "Clifford"
      }, {
        "animal" : "fish",
        "name" : "Nemo"
      } ]
    },
    "xyz789" : {
      "name" : "Jack"
    }
  }

И поэтому мои структуры Swift выглядят так, чтобы соответствовать этому:

struct Human: Codable {
    var name: String!
    var pets: [Pet]?
}

struct Pet: Codable {
    var name: String!
    var animal: Animal!
}

enum Animal: String, Codable {
    case cat
    case dog
    case fish
}

Я пытаюсь декодировать так (используя библиотеку CodableFirebase):

let human = try FirebaseDecoder().decode([Human].self, from: value)

Но я получаю следующую ошибку при попытке кодировать объекты, которые имеют массивы некоторых объектов:

typeMismatch (Swift.Array, Swift.DecodingError.Context (codingPath: [], debugDescription: «Не массив», baseError: nil))

Как я могу правильно кодировать значения словаря в виде массива моих пользовательских объектов Swift?


person vikzilla    schedule 31.08.2018    source источник


Ответы (1)


Есть несколько вопросов:

Прежде всего, вы совершаете распространенную ошибку: вы игнорируете корневой объект JSON, который представляет собой словарь с одним ключом humans. Это ошибка пытается сказать вам.

struct Root : Codable {
    let humans : [Human]
}

let human = try FirebaseDecoder().decode(Root.self, from: value)

Но даже добавление корневой структуры не работает, потому что значение для ключа humans является словарем, обратите внимание на {}

struct Root : Codable {
    let humans : [String:Human]
}

Наконец, никогда, никогда, никогда не объявляйте декодируемые члены структуры как неявные развернутые необязательные параметры, либо они необязательны (да, код компилируется без восклицательных знаков), либо обычные необязательные (?)

struct Human: Codable {
    var name: String
    var pets: [Pet]?
}

struct Pet: Codable {
    var name: String
    var animal: Animal
}

enum Animal: String, Codable {
    case cat, dog, fish
}

Если вам нужен массив Human в структуре Root, а не словарь, вам нужно написать собственный инициализатор

struct Root : Codable{
    let humans : [Human]

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let humanData = try container.decode([String:Human].self, forKey: .humans)
        humans = Array(humanData.values)
    }
}
person vadian    schedule 31.08.2018