首页 > 解决方案 > 如果接收到的值不匹配,则解码字典正确

问题描述

嘿,我有这个 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,但找到了一个字符串/数据。”,underlyingError: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 设置为 nil?

标签: jsonswiftdecodable

解决方案


由于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"

推荐阅读