Swift Codable:嵌套对象错误后继续分析对象

Swift Codable:嵌套对象错误后继续分析对象,swift,swift4,codable,Swift,Swift4,Codable,我的应用程序和很多应用程序一样,从API中检索JSON并使用Swift 4中新的Codable协议进行转换。大多数情况下,这一切都很好,正如预期的那样。然而,有时API会给我发送意想不到的垃圾。类型不正确,数组内部只有null,诸如此类 问题是,所涉及的对象可能很大且复杂,当我解析一个子对象时,它失败了,整个对象都失败了,一直到根。我用一个非常简单的游乐场例子来说明这个概念;实际涉及的对象要复杂得多 let goodJSON = """ { "name": "Fiona Glenanne

我的应用程序和很多应用程序一样,从API中检索JSON并使用Swift 4中新的
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请检查我的答案,并标记它是否是你需要的