如果接收到的值不匹配,则解码字典正确

Posted

技术标签:

【中文标题】如果接收到的值不匹配,则解码字典正确【英文标题】:Decoding a Dictionary if received value does not match correct 【发布时间】:2019-01-24 09:25:24 【问题描述】:

嘿,我有这个 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 但找到一个字符串/数据 而是。”,基础错误:无)

我知道这意味着什么,但我不知道解决方案。 我需要一本带有这种格式的 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。但这不起作用,因为没有候选人产生预期的类型:

没有“解码”候选产生预期的上下文结果类型 '[字符串:任意]'

如果值不是 Int,如何将 Int 设置为 nil?

【问题讨论】:

你必须实现可编码的@PaFi 从 Int @PaFi 将其设为字符串 现在它是一个字符串,但它是一个可以存在任何东西的解决方案,例如 nil 也非常好。感谢您的帮助! @abhishek 你是什么意思?结果应该是 Int? Make it String 它可以工作,当你尝试解码它时它看起来像一个 Int 发现它的字符串这就是为什么并尝试实现 Codable 协议,因为你在 struct 中实现的内容是错误的。 【参考方案1】:

由于Any 不可解码,因此编译器会抱怨。您可以先创建一个enum(来自此answer),如下所示对动态类型进行解码/编码,

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"

【讨论】:

没有别的办法,因为有了这个解决方案,我总是不得不为任何进一步的使用而沮丧..?不过谢谢! @PaFi, use some enum instead of Any 这不起作用,因为Dictionary 具有异构值,因此您不能将其解码为[String:String?][String:Int?]。如果可以,那么您不应该将v 声明为[String:Any?],而是有两个可选属性,一个是[String:String?] 类型,另一个是[String:Int?] 类型,并且始终只分配一个非零值给其中之一。但是,这不是一个简单的情况,因为异构值嵌套在Dictionary 中,因此无法使用JSONDecoder 直接解码。

以上是关于如果接收到的值不匹配,则解码字典正确的主要内容,如果未能解决你的问题,请参考以下文章

python 18:字典的赋值

request接收不存在的参数会是啥结果

Thinkphp Ajax传地址

如何更新字典,以便如果键'a'的值为'c'而不是'c'的键,则附加值'a'?

Ajax传递路径问题及解决

如果值不可用,则获取上个月的值