Reputation: 13768
This is a simplified model that is decoded from JSON:
struct Info: Decodable {
var text: String
var num: Int
}
struct Root: Decodable {
let info: Info
}
Sometimes I need to decode Info.text
or Info.num
only but sometimes both of them and to support all options I've made similar structs for decoding e.g:
// 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
}
This approach produces much cloned code and runtime checks to process the structs so is it possible to decode provided coding keys only with the single struct?
Upvotes: 1
Views: 335
Reputation: 13768
It's possible to provide any contextual information to the decoder with userInfo
property and in this case we can pass an array of coding keys and use this info in the decoding process:
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))
Upvotes: 2