Swift Codable: продолжить анализ объекта после ошибки вложенного объекта - PullRequest
0 голосов
/ 18 сентября 2018

Мое приложение, как и многие другие приложения, извлекает JSON из API и конвертирует его с использованием нового протокола Codable в Swift 4. В большинстве случаев это работает нормально и, как и ожидалось.Однако иногда 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)]

vehicles, как ожидается, будет массивом объектов, но в случае badJSON этоэто единственный объект, который вызывает исключение .typeMismatch и убивает синтаксический анализ прямо там.

Я ищу способ, позволяющий ошибкам, подобным этой, убить синтаксический анализ для потомкатолько объект и разрешить анализ родительского объекта для продолжения.Я пытаюсь сделать это в общем виде, поэтому мне не нужно специально рассматривать каждый объект в моем приложении, чтобы конкретно обрабатывать любые плохие данные, которые предоставляет API.Я не уверен, есть ли хоть какое-то решение для этого, мне не повезло найти что-либо, но это, несомненно, улучшит качество моей жизни, если таковое будет.Спасибо!

1 Ответ

0 голосов
/ 18 сентября 2018

вы можете попробовать настроить 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)
    }
}
...