如何在API的未指定返回数据中使用可解码的

问题描述

当尝试从API解析返回数据时,系统显示“数据格式不正确,因此无法读取”。因为返回不一致。

logo_url具有值时,它是一个对象,请参见下面的示例:

"logo_url": {
                    "mime_type": "image/jpeg","url": "http://google.com"
                },

但是当它确实有值时,它会返回空数组

"logo_url": [],

这就是为什么我收到“由于格式不正确而无法读取数据”的原因。

我的模特

struct Model: Decodable {
    let logo: logo?

    enum CodingKeys: String,CodingKey {
        case logo = "logo_url"
    }
}

struct logo: Decodable {
    
    let mimeType: String?
    let url: String?
    
    enum CodingKeys: String,CodingKey {
        case mimeType = "mime_type"
        case url
    }
}

解决方法

如果您无法更改这个写得不好的API,则需要一个自定义解码器,您基本上会在其中尝试尝试将其解码为所需的类型,并使其失败-将其设置为nil

struct Model: Decodable {
    let logo: Logo?

    enum CodingKeys: String,CodingKey {
        case logo = "logo_url"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        if let logo = try? container.decode(Logo.self,forKey: .logo) {
            self.logo = logo
        } else {
            self.logo = nil
        }
    }
}
,

我个人更喜欢先检查logo_url是否为数组,然后在尝试解码密钥时使用try而不是try?让Swift报告错误(如果发生任何情况)。由于在大多数情况下,您可能想知道为什么解码失败,而不是仅仅得到nil

另外,您可能希望将.convertFromSnakeCase用作keyDecodingStrategy,这样就不必编写额外的代码。

let json2 = """
{
  "logo_url": {
    "mime_type": "image/jpeg","url": "http://google.com"
  }
}
"""

let json3 = "[]"

struct Logo: Decodable {
  let mimeType: String
  let url: String
}

struct Model: Decodable {
  let logo: Logo?

  private enum CodingKeys: String,CodingKey {
      case logoUrl
  }

  init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    if (try? container.decode([String].self,forKey: .logoUrl)) != nil {
      self.logo = nil
    } else {
      self.logo = try container.decode(Logo.self,forKey: .logoUrl)
    }
  }
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase

let result2 = try decoder.decode(Model.self,from: json2.data(using: .utf8)!)
print(result2)

let result3 = try? decoder.decode(Model.self,from: json3.data(using: .utf8)!)
print(result3)