提问人:3366784 提问时间:9/14/2023 更新时间:9/14/2023 访问量:63
解码器是否需要具有密钥的外部数据表示形式?
Does Decoder require an external data representation that has keys?
问:
我做了什么?
我阅读了 Apple 关于该主题的文档。
详
Swift 标准库定义了一种标准化的数据编码和解码方法。
我意识到我遇到的所有示例都从具有 、 和 (custom) 等键的外部数据表示中解码数据。JSON
PLIST
XML
我遇到过一个外部数据表示没有键的用例。
例如,从蓝牙设备读取数据时。您会收到格式为 .Array
UInt8
在探索 Apple 的文档和自定义实现时,我意识到所有外部数据表示都有键。Decoder
我想知道 Apple 的解决方案是否留有足够的回旋余地来处理不使用键的外部数据表示,即 .Array
UInt8
答:
Decoder.container(keyedBy:)
被记录为在没有可供解码的键控数据时抛出错误 ()。在这种程度上,如果 a 表示一种根本无法支持键控数据的格式,那么当请求键控数据时,它总是会抛出错误。DecodingError.typeMismatch
Decoder
Decoder
但是,这样的类型对于任意类型并不是很有用,因为大多数类型默认从键控数据进行编码和解码;这是使用编译器的综合/一致性时的默认行为。Decoder
Encodable
Decodable
如果你使用这样的方法只解码你控制的类型(并且只知道请求非密钥容器),那么完全避免使用API,并编写自己的编码和解码工具可能会更简单、更容易。Decoder
Codable
根据您对格式的描述,听起来这种格式不太可能适合 ,反之亦然。(这也没关系! 并非旨在完美适合所有数据格式。Codable
Codable
评论
JSONDecoder
的当前实现,以及更新包中的重写。JSONDecoder
swift-foundation
JSONSerialization
JSONDecoder
我想你可能误解了可编码 API 的用途。它不是用来编写解析和写入二进制数据的逻辑 - 你仍然需要自己编写。
Codable
允许您通过键控、非键控和单值容器以更抽象的方式进行编码和解码。你可以这样说:
- 获取具有这些密钥的密钥容器
- 获取无密钥容器
- 使用 YYY 密钥对 XXX 进行编码
- 将 XXX 键的值解码为 YYY 类型
- 将 XXX 编码为此无密钥容器的下一个值
- 将此无密钥容器的下一个值解码为 XXX 类型
你写你希望你的模型如何根据这些操作进行编码/解码,然后 / 将对底层二进制数据做相应的事情。Decoder
Encoder
例如,如果要将具有 5 个整数的 JSON 数组解码为具有 5 个属性的结构体,则可以编写:Int
struct MyModel: Decodable {
let a, b, c, d, e: Int
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
a = try container.decode(Int.self)
b = try container.decode(Int.self)
c = try container.decode(Int.self)
d = try container.decode(Int.self)
e = try container.decode(Int.self)
}
}
对于 JSON,表示“准备解码 JSON 数组”,而“decode(Int.self)”表示“将下一个数组元素读取为 ”。请注意,您不必编写如何准确解析 JSON。unkeyedContainer()
Int
Decoder
/Encoder
实现不必支持所有这些操作。如果实现出于任何原因不支持某些方法,则在调用该方法时可能会引发错误。
对于一系列字节,您可以实现自己的仅支持,并且会根据 读取 x 个字节,并将其转换为正确的类型,有点像 BinaryReader。Decoder
unkeyedContainer()
decode(T.self)
T
// just rough sketch of an implementation, not complete
struct _BinaryDecoder: Decoder, UnkeyedDecodingContainer {
var isAtEnd: Bool {
data.count >= currentIndex
}
var currentIndex: Int = 0
func hasNBytes(_ n: Int) -> Bool {
data.count - currentIndex >= n
}
mutating func decode(_ type: Float.Type) throws -> Float {
guard hasNBytes(4) else { throw someError }
let f = data.withUnsafeBytes { buffer in
buffer.loadUnaligned(fromByteOffset: currentIndex, as: Float.self)
}
currentIndex += 4
return f
}
mutating func decode(_ type: Int32.Type) throws -> Int32 {
guard hasNBytes(4) else { throw someError }
let i = data.withUnsafeBytes { buffer in
buffer.loadUnaligned(fromByteOffset: currentIndex, as: Int32.self)
}
currentIndex += 4
return i
}
let data: Data
func unkeyedContainer() throws -> UnkeyedDecodingContainer {
self
}
}
struct BinaryDecoder {
func decode<T: Decodable>(_ type: T.Type, from data: Data) throws -> T {
try T.init(from: _BinaryDecoder(data: data))
}
}
然后,您可以在模型中实现,如下所示:init(from:)
var container = try decoder.unkeyedContainer()
let flags = try container.decode(UInt16.self)
switch flags {
// use container.decode to initialise different properties based on flags
// e.g. if the next bytes represent two 32-bit signed ints, and then a float
case 0:
property1 = try container.decode(Int32.self)
property2 = try container.decode(Int32.self)
property3 = try container.decode(Float.self)
}
但是,如果您只打算解码这一种类型的二进制数据,那么在不使用 .Codable
评论
UnkeyedDecodingContainer
UnkeyedEncodingContainer
Array<UInt8>
Array<UInt8>
Data
UnkeyedDecodingContainer
UnkeyedDecodingContainer
Array<UInt8>
Array<UInt8>
Array<UInt8>
Decodable