Swift Codable:嵌套对象错误后继续分析对象
我的应用程序和很多应用程序一样,从API中检索JSON并使用Swift 4中新的Swift Codable:嵌套对象错误后继续分析对象,swift,swift4,codable,Swift,Swift4,Codable,我的应用程序和很多应用程序一样,从API中检索JSON并使用Swift 4中新的Codable协议进行转换。大多数情况下,这一切都很好,正如预期的那样。然而,有时API会给我发送意想不到的垃圾。类型不正确,数组内部只有null,诸如此类 问题是,所涉及的对象可能很大且复杂,当我解析一个子对象时,它失败了,整个对象都失败了,一直到根。我用一个非常简单的游乐场例子来说明这个概念;实际涉及的对象要复杂得多 let goodJSON = """ { "name": "Fiona Glenanne
Codable
协议进行转换。大多数情况下,这一切都很好,正如预期的那样。然而,有时API会给我发送意想不到的垃圾。类型不正确,数组内部只有null
,诸如此类
问题是,所涉及的对象可能很大且复杂,当我解析一个子对象时,它失败了,整个对象都失败了,一直到根。我用一个非常简单的游乐场例子来说明这个概念;实际涉及的对象要复杂得多
let goodJSON = """
{
"name": "Fiona Glenanne",
"vehicles": [
{
"make": "Saab",
"model": "9-3",
"color": "Black"
},
{
"make": "Hyundai",
"model": "Genesis",
"color": "Blue"
}
]
}
"""
let goodJSONData = goodJSON.data(using: .utf8)!
let badJSON = """
{
"name": "Michael Westen",
"vehicles": {
"make": "Dodge",
"model": "Charger",
"color": "Black"
}
}
"""
let badJSONData = badJSON.data(using: .utf8)!
struct Character: Codable {
let name: String
let vehicles: [Vehicle]
}
struct Vehicle: Codable {
let make: String
let model: String
let color: String
}
do {
let goodCharacter = try JSONDecoder().decode(Character.self, from: goodJSONData)
print(goodCharacter)
} catch {
print(error)
}
do {
let badCharacter = try JSONDecoder().decode(Character.self, from: badJSONData)
print(badCharacter)
} catch DecodingError.typeMismatch(let type, let context) {
print("Got \(type); \(context.debugDescription) ** Path:\(context.codingPath)")
} catch {
print("Caught a different error: \(error)")
}
输出:
Character(name: "Fiona Glenanne", vehicles: [__lldb_expr_20.Vehicle(make: "Saab", model: "9-3", color: "Black"), __lldb_expr_20.Vehicle(make: "Hyundai", model: "Genesis", color: "Blue")])
Got Array<Any>; Expected to decode Array<Any> but found a dictionary instead. ** Path:[CodingKeys(stringValue: "vehicles", intValue: nil)]
字符(名称:“Fiona Glenanne”,车辆:[品牌:“萨博”,型号:“9-3”,颜色:“黑色”),[品牌:“现代”,型号:“Genesis”,颜色:“蓝色”)]
Got数组;应解码数组,但找到了字典。**路径:[编码键(stringValue:“车辆”,intValue:无)]
vehicles
应该是一个对象数组,但在badJSON
的情况下,它是一个单独的对象,这会导致.typemissmatch
异常并终止解析
我要寻找的是一种方法,允许像这样的错误只终止对子对象的解析,并允许继续对父对象的解析。我希望以一种通用的方式来实现这一点,因此我不必对应用程序中的每个对象都进行特殊处理,以专门处理API提供的任何不良数据。我甚至不确定是否有解决办法,我还没有找到任何运气,但如果有,肯定会提高我的生活质量。谢谢 您可以尝试按照注释中的建议自定义init(来自decoder:decoder),它可能是这样的
struct Character: Codable {
let name: String
let vehicles: [Vehicle]
private enum CodingKeys: String, CodingKey { case name, vehicles }
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
do {
let vehicle = try container.decode(Vehicle.self, forKey: .vehicles)
vehicles = [vehicle]
} catch DecodingError.typeMismatch {
vehicles = try container.decode([Vehicle].self, forKey: .vehicles)
}
}
您必须编写一个自定义的
init(来自decoder:decoder)
初始值设定项来处理这些情况。顺便说一下:不要将字符
用作自定义结构名。这可能会导致术语冲突。如果您可以责怪服务所有者发送一致的数据。@瓦迪安感谢您的回复。使用自定义初始值设定项的问题是,我不知道API将如何向我发送格式错误的数据。只是有时候会。试图以防御的方式编写代码可能是一场失败的战斗,因此我试图阻止错误,允许它使嵌套对象失败,并以通用的方式继续处理父对象。还有点好用的字名;这只是我的问题的一个例子,不是真正的代码。在任何一点上,在坏的JSON中,你总是知道什么是坏的吗?@MikeGlass请检查我的答案,并标记它是否是你需要的