Swift 解码错误类型与 `Bool` 类型不一致

问题描述 投票:0回答:2
import Foundation

let json = """
{
    "property": null
}
""".data(using: .utf8)!

struct Program<T: Decodable>: Decodable {
    let property: T
    
    static func decode() {
        do {
            try JSONDecoder().decode(Self.self, from: json)
        } catch {
            print("Error decoding \(T.self): \(error)\n")
        }
    }
}

Program<String>.decode()
Program<Int>.decode()
Program<[Double]>.decode()
Program<[String: Int]>.decode()
Program<Bool>.decode()

对于每种情况,除了

Bool
之外,我们都会得到
valueNotFound
(“无法获取未加密的解码容器 - 找到空值”)错误。根据文档,这是正确的。
仅对于
Bool
,由于某种原因,我们得到
typeMismatch
(“预期解码 Bool 但发现 null 相反。”) 错误。

json swift null decode decodable
2个回答
2
投票

在 Linux (fiddle) 上,

[Double]
生成
valueNotFound
,其他所有内容都会生成
typeMismatch
。这与 swift-core-libs-foundation 的来源一致,其中
unkeyedContainer
(在解码数组时调用)抛出
valueNotFound
(source):

@usableFromInline func unkeyedContainer() throws -> UnkeyedDecodingContainer {
    switch self.json {
    case .array(let array):
        ...
    case .null:
        throw DecodingError.valueNotFound([String: JSONValue].self, DecodingError.Context(
            codingPath: self.codingPath, 
            debugDescription: "Cannot get unkeyed decoding container -- found null value instead"
        ))
    default:
        ...
    }
}

typeMismatch
是由为解码“标量”类型而创建的单值容器抛出的(source)。

func decode(_: Bool.Type) throws -> Bool {
    guard case .bool(let bool) = self.value else {
        throw self.impl.createTypeMismatchError(type: Bool.self, value: self.value)
    }

    return bool
}

因此,我怀疑 macOS 上的

JSONDecoder
并不是故意实现的,以便
Bool
抛出
typeMismatch
和其他
valueNotFound
。似乎首先使用
JSONSerialization
API 将 JSON 解码为
NSDictionary
。因此,
Bool
与其他类型的行为可能会有些奇怪。

我个人认为文档中

typeMismatch
valueNotFound
的措辞足够宽松,当遇到空值时,抛出其中任何一个都是“正确的”。


0
投票

使用 Swift 的

JSONDecoder
解码 JSON 时,如果
String
Int
[Double]
[String: Int]
等非可选类型遇到 null 值,则会抛出 valueNotFound 错误,因为它无法解码 null变成非可选类型。

但是,对于只能为 true 或 false 的

Bool
,遇到 null 会引发 typeMismatch 错误 因为 null 不被视为有效的布尔值

要解决此问题,如果您希望 JSON 中存在空值,则可以将属性类型设置为可选。

struct Program<T: Decodable>: Decodable {
    let property: T?
    
    static func decode() {
        do {
            try JSONDecoder().decode(Self.self, from: json)
        } catch {
            print("Error decoding \(T.self): \(error)\n")
        }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.