Декодирование массива в модели Swift (декодируемый)

Я получаю JSON из API и хочу создать модель для каждой используемой мной конечной точки.

Все конечные точки используют этот формат:

{
  "id": "xxxxxx",
  "result": {…},
  "error": null
}

Ключи:

  • id всегда является строкой
  • error может быть null или объектом с ключами в нем
  • result может быть либо нулевым; объект или массив.

Проблема, с которой я сталкиваюсь, заключается в том, что на одной из конечных точек результаты представляют собой массивы массивов:

{
  "id": "xxxxxx",
  "result": [
      [
          "client_id",
          "name",
          50,
          "status"
      ]
  ],
  "error": null
}

Как вы можете видеть, у меня есть массивы массивов, где значения могут быть либо String, либо Int.

Как вы декодируете это с помощью протокола Decodable, а затем используете эти декодированные значения как String или Int в зависимости от их исходных значений?


person Anthony    schedule 23.12.2018    source источник
comment
Каким объектом может быть "error"?   -  person Robert Dresler    schedule 23.12.2018
comment
Проверьте этот ответ: stackoverflow.com/a/48388443/8447312   -  person emrepun    schedule 23.12.2018
comment
Покажите нам структуру или класс Decodable, который вы используете. Мы не сможем исправить ваш код, если вы не покажете свой код.   -  person EmilioPelaez    schedule 23.12.2018
comment
Самое простое решение — использовать две отдельные модели. Это нормально, что разные конечные точки возвращают разные модели данных.   -  person Sulthan    schedule 24.12.2018


Ответы (1)


import Foundation

let string =  """
{
    "id": "xxxxxx",
    "result": [
        [
            "client_id",
            "name",
            50,
            "status"
        ]
    ],
    "error": null
}
"""

struct Container: Codable {
    let id: String
    let result: [[Result]]
    let error: String?
}

enum Result: Codable {
    case integer(Int)
    case string(String)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let x = try? container.decode(Int.self) {
            self = .integer(x)
            return
        }
        if let x = try? container.decode(String.self) {
            self = .string(x)
            return
        }
        throw DecodingError.typeMismatch(Result.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Result"))
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(self)
    }
}

let jsonData = string.data(using: .utf8)!
let container = try? JSONDecoder().decode(Container.self, from: jsonData)

print(container)

улучшенный ответ @ArinDavoodian.

Чтобы прочитать данные:

container?.result.first?.forEach { object in
    switch object {
    case let .integer(intValue):
        print(intValue)
        break
    case let .string(stringValue):
        print(stringValue)
        break
    }
}

простое решение:

let yourInsideArray = container?.result.first!
for index in 0..<yourInsideArray.count {
let yourObjectInsideThisArray = yourInsideArray[i]
//do some
 switch yourObjectInsideThisArray {
    case let .integer(intValue):
        print(intValue)
        break
    case let .string(stringValue):
        print(stringValue)
        break
    }
}
person Vyacheslav    schedule 23.12.2018
comment
почему бы просто не func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self) } - person Leo Dabus; 24.12.2018
comment
Забавно, думаю, что детская площадка работает, но показывает (12716 раз) вместо 4 - person Leo Dabus; 24.12.2018
comment
вы также можете упростить свой декодер init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() try self = (try? .integer(container.decode(Int.self))) ?? .string(container.decode(String.self)) } - person Leo Dabus; 24.12.2018
comment
На моей площадке всего 4 раза - person Vyacheslav; 24.12.2018
comment
Мне может понадобиться новый файл игровой площадки, возможно, на нем много кода. - person Leo Dabus; 24.12.2018
comment
О вашем втором предложении. Имхо, слишком сложно для производственного кода. - person Vyacheslav; 24.12.2018
comment
Ну вот как я получил ошибку: как вы получаете доступ к данным в массиве результатов? Я просто не могу преобразовать модель в строку: Cannot convert value of type 'HostListModel.Result?' to type 'String' in coercion. Спасибо. - person Anthony; 24.12.2018
comment
@Anthony, вам нужно развернуть перечисления, используя определенный синтаксис Swift. Я обновил свой пост. - person Vyacheslav; 24.12.2018
comment
Спасибо @Вячеслав, все работает. И последнее: как вы читаете данные без цикла forEach? Спасибо еще раз. - person Anthony; 24.12.2018
comment
Потрясающий. Спасибо за быстрые ответы! - person Anthony; 24.12.2018
comment
@Энтони, хорошего дня! - person Vyacheslav; 24.12.2018