gpt4 book ai didi

swift - JSONEncoder 不允许将类型编码为原始值

转载 作者:搜寻专家 更新时间:2023-10-31 21:58:50 26 4
gpt4 key购买 nike

我正在为具有可能关联值的 enum 类型实现 Codable。由于这些对于每种情况都是唯一的,我认为我可以在编码期间不使用 key 输出它们,然后在解码时简单地查看我能得到什么以恢复正确的情况。

这是一个非常精简的人为示例,演示了一种动态类型值:

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/解码器).

最佳答案

有一个错误报告:

https://bugs.swift.org/browse/SR-6163

SR-6163: JSONDecoder cannot decode RFC 7159 JSON

基本上,自 RFC-7159 以来,像 123 这样的值是有效的 JSON,但 JSONDecoder 不支持它。您可以跟进错误报告以查看 future 对此的任何修复。 [该错误已从 iOS 13 开始修复。]

#失败的地方#

它在下面的代码行中失败了,你可以看到如果对象不是数组也不是字典,它就会失败:

https://github.com/apple/swift-corelibs-foundation/blob/master/Foundation/JSONSerialization.swift#L120

open class JSONSerialization : NSObject {
//...

// top level object must be an Swift.Array or Swift.Dictionary
guard obj is [Any?] || obj is [String: Any?] else {
return false
}

//...
}

#解决方法#

您可以使用 JSONSerialization,选项为:.allowFragments:

let jsonText = "123"
let data = Data(jsonText.utf8)

do {
let myString = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
print(myString)
}
catch {
print(error)
}

编码成键值对

最后,您还可以让您的 JSON 对象看起来像这样:

{ "整数": 123456 }

{ "string": "土 bean "}

为此,您需要执行以下操作:

import Foundation 

enum MyValue {
case integer(Int)
case string(String)
}

extension MyValue: Codable {

enum CodingError: Error {
case decoding(String)
}

enum CodableKeys: String, CodingKey {
case integer
case string
}

init(from decoder: Decoder) throws {

let values = try decoder.container(keyedBy: CodableKeys.self)

if let integer = try? values.decode(Int.self, forKey: .integer) {
self = .integer(integer)
return
}

if let string = try? values.decode(String.self, forKey: .string) {
self = .string(string)
return
}

throw CodingError.decoding("Decoding Failed")
}


func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodableKeys.self)

switch self {
case let .integer(i):
try container.encode(i, forKey: .integer)
case let .string(s):
try container.encode(s, forKey: .string)
}
}

}

let theEncodedValue = try! JSONEncoder().encode(MyValue.integer(123456))
let theEncodedString = String(data: theEncodedValue, encoding: .utf8)
print(theEncodedString!) // { "integer": 123456 }
let theDecodedValue = try! JSONDecoder().decode(MyValue.self, from: theEncodedValue)

关于swift - JSONEncoder 不允许将类型编码为原始值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50257242/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com