何时在 Decodable(Swift) 中使用 CodingKeys

When to use CodingKeys in Decodable(Swift)

假设我想解码一个 Person 结构,如下所示。

struct Person: Decodable {

let firstName: String
let lastName: String
let age: Int: String
}

我知道只有用上面的方法才能解码数据。因此,如果上面和下面的实现之间没有区别,我不将属性更改为自定义名称?

请问还有其他情况需要使用CodingKeys吗?除了重命名目的之外,当它们是必要的时,我感到困惑。

struct Person: Decodable {

let firstName: String
let lastName: String
let age: Int: String
}

enum CodingKeys: String, CodingKey {
        
        case firstName
        case lastName
        case age
}

Therefore if I'm not changing the properties to a custom name if there no difference between the above and below implementation?

是的,但是这里有点误会。您拥有的两个实现 字面上 完全相同,因为在第二个实现中永远不会使用 CodingKeys 枚举。要使用,枚举必须嵌套在 Decodable 符合类型(在本例中为 Person):

struct Person: Decodable {
    let firstName: String
    let lastName: String
    let age: Int

    enum CodingKeys: String, CodingKey {    
        case firstName
        case lastName
        case age
    }
}

实际上此实现与您提供的实现没有区别。


Further is there other cases where you want to use CodingKeys?

CodingKeys不仅仅被Decodable使用,它们也被Encodable使用。使用 Encodable 时,使用 CodingKeys 的一个原因是指定应仅序列化实例字段的子集。

您可以以不同的方式使用 CodingKeys,例如,当您知道您在 JSON 中期望的值名称中至少有一个实际上与您的“let 或 var”名称不同时。

示例:

struct Person: Decodable {

let firstName: String
let lastName: String
let age: Int: String
}

enum CodingKeys: String, CodingKey {
        
        case firstName = "first_name"
        case lastName
        case age
}

另一种情况是您使用 class 继承。

总而言之,如果你绝对确定你使用的变量名与你的编码密钥相同(JSON),你可以省略它(但如果你想放,它不会重要),但如果有差异,可能是您的 codingKeys 发生变化,例如大写或使用不同的词,您应该使用枚举将正确的键映射到变量名称。

首先,使用 CodingKeys 有一个成败规则:

  • 如果 JSON 或任何符合 Codable 格式的键完全匹配相应的属性(如您的示例)或转换由适当的 keyDecodingStrategy.

    涵盖
  • 否则你必须指定 all CodingKeys 你需要被解码(另见下面的原因#3)。


使用CodingKeys的三个主要原因:

  1. A Swift variable/property 名称不得以数字开头。如果密钥确实以数字开头,则必须指定兼容的 CodingKey 才能完全解码密钥。
  2. 您想使用不同的 属性 名称。
  3. 你想排除密钥被解码,例如 id 属性 不在 JSON 中并且用 UUID 常量初始化。

如果您实施 init(from decoder 来解码 keyed 容器,则 CodingKeys 是必需的。

如果您的 JSON 具有任意数量的编码键(也称为动态键),CodingKeys 会非常有用。这是一个例子。

import UIKit
// Consider JSON with infinite number of keys: "S001", "S002" and so on
let jsonData = """
{
  "S001": {
    "firstName": "Tony",
    "lastName": "Stark"
  },
  "S002": {
    "firstName": "Peter",
    "lastName": "Parker"
  },
  "S003": {
    "firstName": "Bruce",
    "lastName": "Wayne"
  }
}
""".data(using: .utf8)!



struct Student: Decodable {
    let firstName: String
    let lastName: String
}

struct DecodedArray: Decodable {

    var array: [Student]
    
    // Define DynamicCodingKeys type needed for creating
    // decoding container from JSONDecoder
    private struct DynamicCodingKeys: CodingKey {

        // Use for string-keyed dictionary
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }

        // Use for integer-keyed dictionary
        var intValue: Int?
        init?(intValue: Int) {
            // We are not using this, thus just return nil
            return nil
        }
    }

    init(from decoder: Decoder) throws {

        // 1
        // Create a decoding container using DynamicCodingKeys
        // The container will contain all the JSON first level key
        let container = try decoder.container(keyedBy: DynamicCodingKeys.self)

        var tempArray = [Student]()

        // 2
        // Loop through each key (student ID) in container
        for key in container.allKeys {

            // Decode Student using key & keep decoded Student object in tempArray
            let decodedObject = try container.decode(Student.self, forKey: DynamicCodingKeys(stringValue: key.stringValue)!)
            tempArray.append(decodedObject)
        }

        // 3
        // Finish decoding all Student objects. Thus assign tempArray to array.
        array = tempArray
    }
}

let decodedResult = try! JSONDecoder().decode(DecodedArray.self, from: jsonData)