如果字典数组缺少一个键,如何编写 Codable

How to write Codable if array of dictionary missing one key

在服务器响应中有时会丢失 "tags" 键。我们必须如何为此响应编写可编码结构。

[
  {
    "product_id": 10
    "product_name": "Bulb"
    "tags": ["x", "y", "z"]
  },
  {
    "product_id": 11
    "product_name": "Wire"
  }
]

像这样解码

do {

    // decoding...
    let product_model = try JSONDecoder().decode([ProductItem].self, from: data)

} catch let error {

    print("Product list error(decoder): \(error.localizedDescription)") 
}

// 产品结构

struct ProductItem: Codable {

    // variables
    let product_id: String?
    let product_name: String?
    let tags: [String]?

    // alternative keys...
    private enum CodingKeys: String, CodingKey {

        case product_id
        case product_name
        case tags // what i have to do here
    }
}

看来你JSON不正确。它看起来应该类似于这个:

[
  {
    "product_id": 10,
    "product_name": "Bulb",
    "tags": ["x", "y", "z"]
  },
  {
    "product_id": 11,
    "product_name": "Wire"
  }
]

为此,您的结构应类似于:

struct ProductItem: Codable {

    // variables
    let product_id: Int
    let product_name: String
    let tags: [String]?
}

如果结构中的变量与 JSON 对象中的键同名,则不必提供 CodingKeys。另外,请记住 "product_id": 10, 是一个数字,因此您应该使用 Int 而不是 String.

你的 JSON 少了一些逗号,正确的 JSON 是:

[
    {
        "product_id": 10,
        "product_name": "Bulb",
        "tags": ["x", "y", "z"]
    },
    {
        "product_id": 11,
        "product_name": "Wire"
    }
]

您代码中的 product_id 是一个 String,而在您的 JSON 中,它是一个数字。

Swift 实际上可以像这样处理丢失的键,只要您将 属性 设为可选,您在此处已正确完成。当JSON中没有key时,tags会被赋值为nil。因此,除了将 product_id 更改为 Int.

之外,您实际上不需要做任何其他事情

如果您在解码时使用 convertFromSnakeCase 选项,您的 属性 名称也可以重命名为更 Swifty:

struct ProductItem: Codable {

    let productId: Int?
    let productName: String?
    let tags: [String]?
}

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decoded = try! decoder.decode([ProductItem].self, from: data)
print(decoded[1].tags) // nil

@dahiya_boy 谢谢。下面的代码工作正常。

struct ProductItem : Codable {

        let productId : Int?
        let productName : String?
        let tags : [String]?

        enum CodingKeys: String, CodingKey {
                case productId = "product_id"
                case productName = "product_name"
                case tags = "tags"
        }

        init(from decoder: Decoder) throws {
                let values = try decoder.container(keyedBy: CodingKeys.self)
                productId = try values.decodeIfPresent(Int.self, forKey: .productId)
                productName = try values.decodeIfPresent(String.self, forKey: .productName)
                tags = try values.decodeIfPresent([String].self, forKey: .tags)
        }

}