我正在尝试在Swift 4中解码以下JSON:
{ "token":"RdJY3RuB4BuFdq8pL36w", "permission":"accounts, users", "timout_in":600, "issuer": "Some Corp", "display_name":"John Doe", "device_id":"uuid824fd3c3-0f69-4ee1-979a-e8ab25558421" }
问题是,JSON中的最后2个元素(display_name和device_id)可能存在或可能不存在,或者这些元素可能被命名为完全不同但仍未知的名称,即"fred": "worker", "hours" : 8
display_name
device_id
"fred": "worker", "hours" : 8
所以,我想要实现的解码是已知的东西,即token,permission,timeout_in和issuer任何其他元素(display_name,device_id等),将它们放入一个字典。
token
permission
timeout_in
issuer
我的结构如下所示:
struct AccessInfo : Decodable { let token: String let permission: [String] let timeout: Int let issuer: String let additionalData: [String: Any] private enum CodingKeys: String, CodingKey { case token case permission case timeout = "timeout_in" case issuer } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) token = container.decode(String.self, forKey: .token) permission = try container.decodeIfPresent(String.self, forKey: .permission).components(separatedBy: ",") timeout = try container.decode(Int.self, forKey: . timeout) issuer = container.decode(String.self, forKey: .issuer) // This is where I'm stuck, how do I add the remaining // unknown JSON elements into additionalData? } } // Calling code, breviated for clarity let decoder = JSONDecoder() let accessInfo = try decoder.decode(AccessInfo.self, from: data!)
如果有人可以提供一些指导,那么我能够解码已知结构的一部分,其中JSON也可以包含动态信息。
谢谢
受@matt注释的启发,这是我所使用的完整示例。我扩展了,KeyedDecodingContainer以对未知密钥进行解码,并提供一个参数以过滤出已知密钥CodingKeys。
KeyedDecodingContainer
CodingKeys
样本JSON
迅捷结构
struct AccessInfo : Decodable { let token: String let permission: [String] let timeout: Int let issuer: String let additionalData: [String: Any] private enum CodingKeys: String, CodingKey { case token case permission case timeout = "timeout_in" case issuer } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) token = container.decode(String.self, forKey: .token) permission = try container.decode(String.self, forKey: .permission).components(separatedBy: ",") timeout = try container.decode(Int.self, forKey: . timeout) issuer = container.decode(String.self, forKey: .issuer) // Additional data decoding let container2 = try decoder.container(keyedBy: AdditionalDataCodingKeys.self) self.additionalData = container2. decodeUnknownKeyValues(exclude: CodingKeys.self) } } private struct AdditionalDataCodingKeys: CodingKey { var stringValue: String init?(stringValue: String) { self.stringValue = stringValue } var intValue: Int? init?(intValue: Int) { return nil } }
KeyedDecodingContainer扩展
extension KeyedDecodingContainer where Key == AdditionalDataCodingKeys { func decodeUnknownKeyValues<T: CodingKey>(exclude keyedBy: T.Type) -> [String: Any] { var data = [String: Any]() for key in allKeys { if keyedBy.init(stringValue: key.stringValue) == nil { if let value = try? decode(String.self, forKey: key) { data[key.stringValue] = value } else if let value = try? decode(Bool.self, forKey: key) { data[key.stringValue] = value } else if let value = try? decode(Int.self, forKey: key) { data[key.stringValue] = value } else if let value = try? decode(Double.self, forKey: key) { data[key.stringValue] = value } else if let value = try? decode(Float.self, forKey: key) { data[key.stringValue] = value } else { NSLog("Key %@ type not supported", key.stringValue) } } } return data } }
呼叫码
let decoder = JSONDecoder() let accessInfo = try decoder.decode(AccessInfo.self, from: data!) print("Token: \(accessInfo.token)") print("Permission: \(accessInfo.permission)") print("Timeout: \(accessInfo.timeout)") print("Issuer: \(accessInfo.issuer)") print("Additional Data: \(accessInfo.additionalData)")
输出量
Token: RdJY3RuB4BuFdq8pL36w Permission: ["accounts", "users"] Timeout: 600 Issuer: "Some Corp" Additional Data: ["display_name":"John Doe", "device_id":"uuid824fd3c3-0f69-4ee1-979a-e8ab25558421"]