Swift Decodable,端点返回完全不同的类型

时间:2019-11-10 20:43:58

标签: swift codable decodable swift-extensions jsondecoder

在使用我正在使用的API的情况下,根据调用是否成功,一个API端点可以返回完全不同的响应。
如果成功,API端点将在根中返回一个请求的对象数组,如下所示:

[
    {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3"
    },
    {
        "key1": "value1",
        "key2": "value2",
        "key3": "value3"
    },
    ...
]

我通常用try JSONDecoder().decode([Object].self, from: data)解码

如果出现错误,API端点将返回完全不同的内容,如下所示:

{
    "error": "value1",
    "message": "value2",
    "status": "value3"
}

,使用try JSONDecoder().decode([Object].self, from: data)解码通常会失败。

现在,我的问题是,有没有一种方法可以对错误响应密钥进行解码,这种方式(我会说不是通常构建的API)没有创建一个名为Objects复数对象-该对象具有可选属性errormessagestatus,例如{{ 1}}。
我的想法到了扩展数组objects到某种程度上尝试解码where Element == Objecterrormessage的地步,但是我遇到了status。也许不可能那样做,所以任何其他甚至完全不同的建议都将受到欢迎。

4 个答案:

答案 0 :(得分:1)

您可以尝试解码[Object],如果失败,请使用错误密钥解码另一个结构。

答案 1 :(得分:0)

引入“抽象”结构作为解码调用的接收者,并让该结构解码正确的类型并返回Result对象

enum ApiErrorEnum: Error {
    case error(ApiError)
}

struct ResponseHandler: Decodable {
    let result: Result<[ApiResult], ApiErrorEnum>

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()

        do {
            let values = try container.decode([ApiResult].self)
            result = .success(values)
        } catch {
            let apiError = try container.decode(ApiError.self)
            result = .failure(.error(apiError))
        }
    }
}

,然后可以将其用于例如闭包

func decodeApi(_ data: Data, completion: @escaping (Result<[ApiResult], ApiErrorEnum>?, Error?) -> ()) {
    do {
        let decoded = try JSONDecoder().decode(ResponseHandler.self, from: data)
        completion(decoded.result, nil)
    } catch {
        completion(nil, error)
    }
}

答案 2 :(得分:0)

利用do-catch块可以尝试解码一种类型,如果解码失败,则尝试另一种选择。我非常喜欢使用枚举来处理结果...

struct Opt1: Codable {
   let key1, key2, key3: String
}

struct Opt2: Codable {
   let error, message, status: String
}

enum Output {
   case success([Opt1])
   case failure(Opt2)
}

let decoder = JSONDecoder()
let data = json.data(using: .utf8)!
var output: Output

do {
   let opt1Array = try decoder.decode([Opt1].self, from: data)
   output = .success(opt1Array)
} catch {
   let opt2 = try decoder.decode(Opt2.self, from: data)
   output = .failure(opt2)
}

答案 3 :(得分:0)

我的建议是将JSON的根对象解码为具有关联类型的枚举

struct Item : Decodable {
    let key1, key2, key3 : String
}

struct ResponseError  : Decodable {
    let error, message, status : String
}

enum Response : Decodable {
    case success([Item]), failure(ResponseError)

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        do {
            self = .success(try container.decode([Item].self))
        } catch DecodingError.typeMismatch {
            self = .failure(try container.decode(ResponseError.self))
        }
    }
}

并使用它

do {
    let result = try JSONDecoder().decode(Response.self, from: data)
    switch result {
        case .success(let items): print(items)
        case .failure(let error): print(error.message)
    }
} catch {
    print(error)
}

优良作法是仅捕获特定的.typeMismatch错误,并将其他错误立即移交给调用方。