为具有失败响应的字典数组实现 Codable
Implement Codable for an Array of Dictionary with failable responses
我的 JSON
回复如下:
{
"data": [
{
"unknown-key-c3e7f0": {
"date_time": 1546944854000,
"medication": "f4f25ea4-0607-4aac-b85a-edf40cc7d5b6",
"record": {
"status": "never"
}
},
"unknown-key-619d40": {
"date_time": 1546944854000,
"medication": "deef2278-f176-418f-ac34-c65fa54e712c",
"record": {
"status": "always"
}
},
"event": "06b445b9-dae0-48a1-85e4-b9f48c9a2349",
"created": 1546949155020,
"user": "8fb3fcd1-ffe6-4fd9-89b8-d22b1653cb6a",
"id": "1546944855000",
"type": "compliance"
},
{
"unknown-key-619d40": {
"date_time": 1546944975000,
"medication": "deef2278-f176-418f-ac34-c65fa54e712c",
"record": {
"status": "sometimes"
}
},
"event": "7309d8e9-b71c-4068-b278-0ae6d91a57a6",
"created": 1546946798407,
"user": "8fb3fcd1-ffe6-4fd9-89b8-d22b1653cb6a",
"id": "1546944975000",
"type": "compliance"
}
}
从上面的响应中,我想获取未知键及其值。未知键的值是一种名为 Record
的自定义类型,它符合 Codable
协议。
我创建了这个结构来解析数据
struct RecordSuper: Codable
{
var data: [[String: Record]]
}
所以,我想过滤所有其他键,如 event, created, user
等,我在响应中得到这些键并仅保存未知键和值。
请建议如何使用 codable 解析它。
我已经阅读了这个答案以及答案的第三条评论中建议的变体。
这个答案展示了如何过滤数组中不正确的数据,以免丢失正确的数据。我正在尝试做类似的事情。
例如,我想丢弃 event
键,因为它是 String
类型而不是 Record
类型。
上面的答案将丢弃整个字典,因为所有字典都有不正确的数据,如 event
。最后,我得到一个空数组。
提前致谢。
这是一个广泛基于此 of Rob Napier 的解决方案。
TitleKey
和两个 Decoder
扩展的目标是将具有任意键的字典映射到数组,并将键添加为 title
属性.
struct TitleKey: CodingKey {
let stringValue: String
init?(stringValue: String) { self.stringValue = stringValue }
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
extension Decoder {
func currentTitle() throws -> String {
guard let titleKey = codingPath.last as? TitleKey else {
throw DecodingError.dataCorrupted(.init(codingPath: codingPath,
debugDescription: "Not in titled container"))
}
return titleKey.stringValue
}
}
extension Decoder {
func decodeTitledElements<Element: Decodable>(_ type: Element.Type) throws -> [Element] {
let titles = try container(keyedBy: TitleKey.self)
return titles.allKeys.compactMap { title in
return try? titles.decode(Element.self, forKey: title)
}
}
}
我修改了 decodeTitledElements
函数以仅解码那些值代表 RecordSuper
结构过滤其他键的字典。
这是结构。
struct Root : Decodable {
let data : [Containers]
}
struct Containers: Decodable {
let containers: [RecordSuper]
init(from decoder: Decoder) throws {
self.containers = try decoder.decodeTitledElements(RecordSuper.self)
}
}
struct RecordSuper : Decodable {
let title : String
let dateTime : Date
let medication : String
let record : Record
enum CodingKeys: String, CodingKey {
case dateTime = "date_time", medication, record
}
init(from decoder: Decoder) throws {
self.title = try decoder.currentTitle()
let container = try decoder.container(keyedBy: CodingKeys.self)
self.dateTime = try container.decode(Date.self, forKey: .dateTime)
self.medication = try container.decode(String.self, forKey: .medication)
self.record = try container.decode(Record.self, forKey: .record)
}
}
struct Record : Decodable {
let status : String
}
现在解码 JSON 假设 jsonData
是 JSON 为 Data
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .millisecondsSince1970
let result = try decoder.decode(Root.self, from: jsonData
print(result.data)
我的 JSON
回复如下:
{
"data": [
{
"unknown-key-c3e7f0": {
"date_time": 1546944854000,
"medication": "f4f25ea4-0607-4aac-b85a-edf40cc7d5b6",
"record": {
"status": "never"
}
},
"unknown-key-619d40": {
"date_time": 1546944854000,
"medication": "deef2278-f176-418f-ac34-c65fa54e712c",
"record": {
"status": "always"
}
},
"event": "06b445b9-dae0-48a1-85e4-b9f48c9a2349",
"created": 1546949155020,
"user": "8fb3fcd1-ffe6-4fd9-89b8-d22b1653cb6a",
"id": "1546944855000",
"type": "compliance"
},
{
"unknown-key-619d40": {
"date_time": 1546944975000,
"medication": "deef2278-f176-418f-ac34-c65fa54e712c",
"record": {
"status": "sometimes"
}
},
"event": "7309d8e9-b71c-4068-b278-0ae6d91a57a6",
"created": 1546946798407,
"user": "8fb3fcd1-ffe6-4fd9-89b8-d22b1653cb6a",
"id": "1546944975000",
"type": "compliance"
}
}
从上面的响应中,我想获取未知键及其值。未知键的值是一种名为 Record
的自定义类型,它符合 Codable
协议。
我创建了这个结构来解析数据
struct RecordSuper: Codable
{
var data: [[String: Record]]
}
所以,我想过滤所有其他键,如 event, created, user
等,我在响应中得到这些键并仅保存未知键和值。
请建议如何使用 codable 解析它。
我已经阅读了这个答案以及答案的第三条评论中建议的变体。
这个答案展示了如何过滤数组中不正确的数据,以免丢失正确的数据。我正在尝试做类似的事情。
例如,我想丢弃 event
键,因为它是 String
类型而不是 Record
类型。
上面的答案将丢弃整个字典,因为所有字典都有不正确的数据,如 event
。最后,我得到一个空数组。
提前致谢。
这是一个广泛基于此
TitleKey
和两个 Decoder
扩展的目标是将具有任意键的字典映射到数组,并将键添加为 title
属性.
struct TitleKey: CodingKey {
let stringValue: String
init?(stringValue: String) { self.stringValue = stringValue }
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
}
extension Decoder {
func currentTitle() throws -> String {
guard let titleKey = codingPath.last as? TitleKey else {
throw DecodingError.dataCorrupted(.init(codingPath: codingPath,
debugDescription: "Not in titled container"))
}
return titleKey.stringValue
}
}
extension Decoder {
func decodeTitledElements<Element: Decodable>(_ type: Element.Type) throws -> [Element] {
let titles = try container(keyedBy: TitleKey.self)
return titles.allKeys.compactMap { title in
return try? titles.decode(Element.self, forKey: title)
}
}
}
我修改了 decodeTitledElements
函数以仅解码那些值代表 RecordSuper
结构过滤其他键的字典。
这是结构。
struct Root : Decodable {
let data : [Containers]
}
struct Containers: Decodable {
let containers: [RecordSuper]
init(from decoder: Decoder) throws {
self.containers = try decoder.decodeTitledElements(RecordSuper.self)
}
}
struct RecordSuper : Decodable {
let title : String
let dateTime : Date
let medication : String
let record : Record
enum CodingKeys: String, CodingKey {
case dateTime = "date_time", medication, record
}
init(from decoder: Decoder) throws {
self.title = try decoder.currentTitle()
let container = try decoder.container(keyedBy: CodingKeys.self)
self.dateTime = try container.decode(Date.self, forKey: .dateTime)
self.medication = try container.decode(String.self, forKey: .medication)
self.record = try container.decode(Record.self, forKey: .record)
}
}
struct Record : Decodable {
let status : String
}
现在解码 JSON 假设 jsonData
是 JSON 为 Data
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .millisecondsSince1970
let result = try decoder.decode(Root.self, from: jsonData
print(result.data)