【发布时间】:2023-03-21 22:10:02
【问题描述】:
我正在为具有可能关联值的enum 类型实现Codable。由于这些对于每种情况都是独一无二的,我认为我可以在编码期间不带密钥地输出它们,然后简单地看看我在解码时可以得到什么以恢复正确的情况。
这是一个非常精简的人为示例,演示了一种动态类型的值:
enum MyValueError : Error { case invalidEncoding }
enum MyValue {
case bool(Bool)
case float(Float)
case integer(Int)
case string(String)
}
extension MyValue : Codable {
init(from theDecoder:Decoder) throws {
let theEncodedValue = try theDecoder.singleValueContainer()
if let theValue = try? theEncodedValue.decode(Bool.self) {
self = .bool(theValue)
} else if let theValue = try? theEncodedValue.decode(Float.self) {
self = .float(theValue)
} else if let theValue = try? theEncodedValue.decode(Int.self) {
self = .integer(theValue)
} else if let theValue = try? theEncodedValue.decode(String.self) {
self = .string(theValue)
} else { throw MyValueError.invalidEncoding }
}
func encode(to theEncoder:Encoder) throws {
var theEncodedValue = theEncoder.singleValueContainer()
switch self {
case .bool(let theValue):
try theEncodedValue.encode(theValue)
case .float(let theValue):
try theEncodedValue.encode(theValue)
case .integer(let theValue):
try theEncodedValue.encode(theValue)
case .string(let theValue):
try theEncodedValue.encode(theValue)
}
}
}
let theEncodedValue = try! JSONEncoder().encode(MyValue.integer(123456))
let theEncodedString = String(data: theEncodedValue, encoding: .utf8)
let theDecodedValue = try! JSONDecoder().decode(MyValue.self, from: theEncodedValue)
但是,这在编码阶段给了我一个错误,如下所示:
"Top-level MyValue encoded as number JSON fragment."
问题似乎是,无论出于何种原因,JSONEncoder 都不允许将不是可识别原语的顶级类型编码为单个原语值。如果我将singleValueContainer() 更改为unkeyedContainer(),那么它工作得很好,除了生成的JSON 是一个数组,而不是单个值,或者我可以使用一个键控容器,但这会产生一个带有增加了密钥的开销。
我在这里尝试用单值容器做的事情是不可能的吗?如果没有,我可以使用一些解决方法吗?
我的目标是让我的类型Codable 的开销最小,而不仅仅是JSON(该解决方案应该支持任何有效的Encoder/Decoder)。
【问题讨论】:
-
您的类型中的可编码实现没有问题(尽管您应该在解码时交换浮点/整数大小写,否则任何整数都会陷入浮点大小写),JSONEncoder/Decoder 只是没有t 支持对不是数组/字典的顶级对象进行编码。如果当您实际使用此类型时,您将其用作另一个可编码对象的属性,那么它将正常工作。
-
一个简单的解决方案是将值包装在一个数组中,例如
let theEncodedValue = try! JSONEncoder().encode([MyValue.integer(123456)])然后用let theDecodedValue = try! JSONDecoder().decode([MyValue].self, from: theEncodedValue)解码 -
实际上,进行该更改表明@dan 是正确的,它确实将数字解析为浮点数,而不是整数。