Swift Codable работает не так, как ожидалось?

Vishal16 спросил: 31 июля 2018 в 09:44 в: json
{
"responseBody": {
    "table": {
        "data": [
            [
                "Forth Record",
                null,
                0,
                "2018-08-23T18:30:01.000+0000",
                0,
                0,
                "HCL",
                "b74d10ef4fe246948cd036071787ff25"
            ],
            [
                "Third Record",
                "Testing custom object record 3",
                348,
                "2018-08-22T18:30:01.000+0000",
                36.45,
                4545.45,
                "HCL",
                "139fdba94bb143849fef220f105d66d0"
            ],
            [
                "Second Record",
                "Testing custom object record 2",
                56,
                "2018-08-22T18:30:01.000+0000",
                6577.67,
                567.67,
                "HAL",
                "606a06c93ea2473fb832e5daafa02df9"
            ],
            [
                "First Record",
                "Testing custom object record",
                75,
                "2018-08-22T18:30:01.000+0000",
                47.54,
                67.63,
                "HBL",
                "29c4125f3fa947b9b252318305e986c7"
            ]
        ]
    }
}
}

Я хочу разобрать выше JSON с помощью быстрого 4 Codable. Пожалуйста, посмотрите мою иерархию объектов ниже

//ViewRecordResponse.swift
import Foundation
struct ViewRecordResponse : Codable {
    let responseBody : ViewRecord?    enum CodingKeys: String, CodingKey {
        case responseBody = "responseBody"
    }    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        responseBody = try values.decodeIfPresent(ViewRecord.self, forKey: .responseBody)
    }
}//ViewRecord.swift
import Foundation
struct ViewRecord : Codable {
    let table : Table?    enum CodingKeys: String, CodingKey {
        case table = "table"
    }    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        table = try values.decodeIfPresent(Table.self, forKey: .table)
    }
}//Table.swift
import Foundation
struct Table : Codable {
    let data : [[String?]]?    enum CodingKeys: String, CodingKey {
        case data = "data"
    }    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        data = try values.decodeIfPresent([[String?]].self, forKey: .data)
    }
}

, но когда я пытаюсь декодировать JSON с помощью Codeable Mapping, я получил сообщение об ошибке

t читать, потому что он отсутствует.

Данные не могут быть прочитаны, потому что они не в правильном формате.

код для декодирования для объекта JSON

do {
    let jsonDecoder = JSONDecoder()
    let response = try jsonDecoder.decode(ViewRecordResponse.self, from: data)
} catch let error {
    print(error.localizedDescription)
}
Printing description of data:
▿ 557 bytes
  - count : 557
▿ pointer : 0x0000000104a23005
  - pointerValue : 4372705285
"data": [
            [
                456,
                31.04,
                10000,
                "Dummy Data",
                "text area dummy",
                "2018-08-27T18:30:01.000+0000",
                "UE",
                "4e67d5c02b0147b1bcfc00f459c0c612"
            ],

1 ответ

Есть решение
vadian ответил: 31 июля 2018 в 10:17

Основная проблема заключается в том, что вложенный массив в data не [[String?]], также есть Int и Double значения. Это, скорее всего, причина ошибки.

Я предлагаю использовать (довольно недооцененный) unkeyedContainer для декодирования внутреннего массива в структуру. decodeIfPresent обрабатывает значение null.

Ваши структуры могут быть просто найдены, ключи кодирования и инициализаторы могут быть опущены

struct ViewRecordResponse : Codable {
    let responseBody : ViewRecord
}struct ViewRecord : Codable {
    let table : Table
}struct Table : Codable {
    let data : [Record]
}struct Record : Codable {
    let name : String
    let description : String?
    let index : Int
    let date : String
    let double1 : Double
    let double2 : Double
    let abbrev : String
    let sha : String    init(from decoder: Decoder) throws {
        var arrayContrainer = try decoder.unkeyedContainer()
        name = try arrayContrainer.decode(String.self)
        description = try arrayContrainer.decodeIfPresent(String.self)
        index = try arrayContrainer.decode(Int.self)
        date = try arrayContrainer.decode(String.self)
        double1 = try arrayContrainer.decode(Double.self)
        double2 = try arrayContrainer.decode(Double.self)
        abbrev = try arrayContrainer.decode(String.self)
        sha = try arrayContrainer.decode(String.self)
    }
}

Я не рекомендую помещать каждую структуру в отдельный файл, поскольку они принадлежат друг другу.

Vishal16 ответил: 31 июля 2018 в 10:12
Хорошо, я постараюсь.
Vishal16 ответил: 02 августа 2018 в 04:02
Еще одна проблема: запись не соответствует какому-либо конкретному формату, это может быть int, int, double, double и string, и ваше решение работает только тогда, когда данные записи JSON поступают в определенном формате?
vadian ответил: 02 августа 2018 в 04:36
Чем больше гибкости, тем больше кода вы должны написать. Если запись не имеет определенного формата, использование Codable не имеет смысла. С другой стороны, очень и очень плохая привычка отправлять такие противоречивые данные JSON в массиве.
vadian ответил: 02 августа 2018 в 04:43
Затем я предлагаю отбросить Codable, использовать обычный JSONSerialization и проверить типы. Вы можете воспользоваться магией Codable, только если получите непротиворечивые данные.