Swift 可使用以编程方式提供的编码密钥进行解码
Swift decodable with programatically provided coding keys
这是从JSON解码的简化模型:
struct Info: Decodable {
var text: String
var num: Int
}
struct Root: Decodable {
let info: Info
}
有时我只需要解码 Info.text
或 Info.num
,但有时两者都需要解码,并且为了支持所有选项,我制作了类似的解码结构,例如:
// For text only
struct InfoText: Decodable {
var text: String
}
struct RootText: Decodable {
let info: InfoText
}
// For num only
struct InfoNum: Decodable {
var num: Int
}
struct RootNum: Decodable {
let info: InfoNum
}
这种方法会产生大量克隆代码和运行时检查来处理结构,因此是否可以仅使用单个结构解码提供的编码密钥?
可以使用 userInfo
属性 向解码器提供任何上下文信息,在这种情况下,我们可以传递一组编码密钥并在解码过程中使用此信息:
struct Info: Decodable {
var text: String?
var num: Int?
static var keys = CodingUserInfoKey(rawValue: "keys")!
enum CodingKeys: String, CodingKey {
case text, num
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
guard let keys = decoder.userInfo[Self.keys] as? [CodingKeys] else {
return
}
if keys.contains(.text) {
text = try container.decode(String.self, forKey: .text)
}
if keys.contains(.num) {
num = try container.decode(Int.self, forKey: .num)
}
}
}
struct Root: Decodable {
let info: Info
}
let json = #"{ "info" : { "text": "Hello", "num": 20 } }"#.data(using: .utf8)!
let decoder = JSONDecoder()
let keys: [Info.CodingKeys] = [.text]
decoder.userInfo[Info.keys] = keys
let root = try decoder.decode(Root.self, from: json)
print(root)
// Outputs:
Root(info: Info(text: Optional("Hello"), num: nil))
这是从JSON解码的简化模型:
struct Info: Decodable {
var text: String
var num: Int
}
struct Root: Decodable {
let info: Info
}
有时我只需要解码 Info.text
或 Info.num
,但有时两者都需要解码,并且为了支持所有选项,我制作了类似的解码结构,例如:
// For text only
struct InfoText: Decodable {
var text: String
}
struct RootText: Decodable {
let info: InfoText
}
// For num only
struct InfoNum: Decodable {
var num: Int
}
struct RootNum: Decodable {
let info: InfoNum
}
这种方法会产生大量克隆代码和运行时检查来处理结构,因此是否可以仅使用单个结构解码提供的编码密钥?
可以使用 userInfo
属性 向解码器提供任何上下文信息,在这种情况下,我们可以传递一组编码密钥并在解码过程中使用此信息:
struct Info: Decodable {
var text: String?
var num: Int?
static var keys = CodingUserInfoKey(rawValue: "keys")!
enum CodingKeys: String, CodingKey {
case text, num
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
guard let keys = decoder.userInfo[Self.keys] as? [CodingKeys] else {
return
}
if keys.contains(.text) {
text = try container.decode(String.self, forKey: .text)
}
if keys.contains(.num) {
num = try container.decode(Int.self, forKey: .num)
}
}
}
struct Root: Decodable {
let info: Info
}
let json = #"{ "info" : { "text": "Hello", "num": 20 } }"#.data(using: .utf8)!
let decoder = JSONDecoder()
let keys: [Info.CodingKeys] = [.text]
decoder.userInfo[Info.keys] = keys
let root = try decoder.decode(Root.self, from: json)
print(root)
// Outputs:
Root(info: Info(text: Optional("Hello"), num: nil))