Swift Decodable JSON словарь с неоднородным массивом

У меня есть JSON, который возвращается в следующем формате,

{
"Random Word": [
    [
        "2017-08-10",
        6
    ],
    [
        "2017-08-11",
        6
    ],
    [
        "2017-08-15",
        4
    ]
],
"Another Random Word": [
    [
        "2017-08-10",
        4
    ],
    [
        "2017-08-11",
        4
    ],
    [
        "2017-08-12",
        1
    ],
    [
        "2017-08-14",
        2
    ],
    [
        "2017-08-15",
        4
    ],
    [
        "2017-08-16",
        1
    ]
]
}

Проблема в том, что "ключ" будет отличаться каждый раз, а "значение" содержит гетерогенный массив строк (которые должны быть преобразованы в даты) и целочисленные значения.

Есть ли способ использовать Decodable протокол Swift, чтобы превратить это в объекты?

Вот структура, которая может быть расшифрована как,

struct MyJSONData: Decodable {

    var myInfo: Dictionary<String, [[Any]]>?
    ...
}

Однако, если есть лучший способ структурировать структуру, я все уши!

Заранее спасибо.

2 ответа

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

Если это так, вы можете использовать следующее решение:

struct MyJSONData: Decodable {
    var dates = [Any]()

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()

        // Only use first item
        let stringItem = try container.decode(String.self)
        dates.append(stringItem)
        let numberItem = try container.decode(Int.self)
        dates.append(numberItem)
    }
}

let decoded = try! JSONDecoder().decode([String : [MyJSONData]].self, from: jsonData).values
// Returns an Array of MyJSONData

Рабочее решение: http://swift.sandbox.bluemix.net/

Я работал с API, который кодировал массив JSON с разнородными данными, подобными вашим, но даже порядок столбцов не был известен заранее:(

В общем, я настоятельно рекомендую не хранить данные в разнородных массивах. Вы очень быстро забудете, какой индекс обозначает какое свойство, не говоря уже о постоянном приведении туда-сюда. Вместо этого создайте модель данных для ее хранения при декодировании из массива.

Еще одна вещь, которую стоит отметить, это то, что ваша дата не то, что JSONDecoder ожидает по умолчанию. Ожидается формат ISO 8601 (yyyy-MM-ddTHH:mm:ssZ) тогда как компонент времени отсутствует в вашей строке даты. Ты можешь сказать JSONDecoder что делать, поставляя кастом DateFormatter:

struct WordData: Decodable {
    var date: Date
    var anInt: Int

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        self.date = try container.decode(Date.self)
        self.anInt = try container.decode(Int.self)
    }
}

var dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_us_POSIX")
dateFormatter.timeZone = TimeZone(identifier: "UTC")
dateFormatter.dateFormat = "yyyy-MM-dd"

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)

let words = try decoder.decode([String: [WordData]].self, from: jsonData)
Другие вопросы по тегам