【问题标题】:Swift Decodable, Endpoint returns completely different typesSwift Decodable,Endpoint 返回完全不同的类型
【发布时间】:2020-03-06 15:10:53
【问题描述】:

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

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

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

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

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

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

现在,我的问题是,有没有办法在这种(我会说不是通常架构的 API)中解码错误响应键,没有创建一个名为Objects 的-我称之为- 复数 对象,该对象将具有可选属性errormessagestatus,例如objects
我的想法是在某个地方扩展数组where Element == Object,并以某种方式尝试解码errormessagestatus,但我正在打Conformance of 'Array<Element>' to protocol 'Decodable' was already stated in the type's module 'Swift'。也许甚至不可能那样做,所以任何其他,甚至完全不同的建议都会非常受欢迎。

【问题讨论】:

    标签: swift codable decodable swift-extensions jsondecoder


    【解决方案1】:

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

    【讨论】:

    • 感谢您的回复,但在某些情况下它不起作用。例如,对于泛型,当整个网络类集(如 APIRouter)期望接收应该解码的对象类型时。喜欢let objectsAPI = APIRouter<[Object]>()
    【解决方案2】:

    引入一个“抽象”结构,它是解码调用的接收者,让该结构解码正确的类型并返回一个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)
        }
    }
    

    【讨论】:

    • 使用Result 是正确的方法。我建议您完全摆脱第一个变体,或者至少将其更改为枚举。
    【解决方案3】:

    使用 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)
    }
    

    【讨论】:

      【解决方案4】:

      我的建议是将 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 错误并将其他错误立即移交给调用者。

      【讨论】:

        猜你喜欢
        • 2021-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多