Расшифровка словаря, если полученное значение не соответствует правильному - PullRequest
0 голосов
/ 24 января 2019

Эй, у меня есть этот Json для примера:

{"v":{"value1":1548303671,"value2":"invalid","value3":"invalid"}}

И модель класса:

struct VersionApi: Decodable {
    let data: NestedData?
    let v: [String:Int?]

    enum CodingKeys: String, CodingKey {
        case data = "data"
        case v = "v"
    }
}

При попытке декодирования я получаю это сообщение об ошибке:

debugDescription: "Ожидается декодировать Int, но найдена строка / данные вместо. ", underError: nil)

Я знаю, что это значит, но я не знаю решения. Мне нужен словарь с Ints в этом формате: [String:Int?]. Я пытался написать собственный инициализатор, как это:

init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        data = try values.decode(NestedData, forKey: .data)
        let vID: [String:Any] = try values.decode([String:Any].self, forKey: .v)
        v = try values.decode([String:Int?].self, forKey: .v)

    }

Затем я хотел просмотреть словарь, и если Any не Int, я бы хотел установить nil. Но это не работает, поскольку ни один из кандидатов не выдает ожидаемый тип:

Ни один из кандидатов 'декодирования' не приводит к ожидаемому контекстуальному типу результата '[String: Any]'

Как я могу установить Int в ноль, если значение не является Int?

1 Ответ

0 голосов
/ 24 января 2019

Поскольку Any не декодируется, компилятор жалуется.Сначала вы можете создать enum (из этого ответа ), как показано ниже для декодирования / кодирования динамического типа,

enum BiType<L: Codable, R: Codable>: Codable {
    case left(L)
    case right(R)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        if let left = try? container.decode(L.self) {
            self = .left(left)
        } else if let right = try? container.decode(R.self) {
            self = .right(right)
        } else {
            throw DecodingError
                .typeMismatch(
                    BiType<L, R>.self,
                    .init(codingPath: decoder.codingPath,
                          debugDescription: "Expected either `\(L.self)` or `\(R.self)`"))
        }
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
        case let .left(left):
            try container.encode(left)
        case let .right(right):
            try container.encode(right)
        }
    }
}

Теперь вы можете обновить VersionApi для использования этого типа,

struct VersionApi: Decodable {
    let data: NestedData?
    let v: [String: BiType<String, Int>]

    enum CodingKeys: String, CodingKey {
        case data = "data"
        case v = "v"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        data = try values.decode(NestedData.self, forKey: .data)
        v = try values.decode([String: BiType<String, Int>].self, forKey: .v)
    }
}

Пример

let data = """
{"v":{"value1":1548303671,"value2":"invalid","value3":"invalid"}}
""".data(using: .utf8)!

do {
    let v = try JSONDecoder().decode(VersionApi.self, from: data)
    v.v.values.forEach({ (type) in
        switch type {
        case .left(let left):
            debugPrint(left)
        case .right(let right):
            debugPrint(right)
        }
    })
} catch {
    debugPrint(error)
}

Выход

1548303671 
"invalid" 
"invalid"
...