何时在 Decodable(Swift) 中使用 CodingKeys

问题描述 投票:0回答:5

假设我想按如下方式解码 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
}

swift codable decodable
5个回答
6
投票

首先,使用

CodingKeys
有一个成败规则:

  • 如果 JSON(或任何符合

    CodingKeys
    的格式)键与相应的属性完全匹配(如您的示例中所示),或者转换由适当的
    Codable
     涵盖,则可以完全省略 
    keyDecodingStrategy

  • 否则你必须指定all

    CodingKeys
    你需要解码(另见下面的原因#3)。


使用

CodingKeys
的三大理由:

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

如果您实现

CodingKeys
来解码
keyed
容器,则 init(from decoder 是必需的。


6
投票

您可以以不同的方式使用 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
}

其他情况是当您使用类继承时。

总之,如果你绝对确定你使用的变量名与你的编码密钥(JSON)相同,你可以省略它(但如果你想放它,也没关系),但如果有区别,也许您的codingKeys发生了变化,例如大写或使用不同的单词,您应该使用枚举将正确的键与变量名称映射。


1
投票

如果您有一个包含任意数量的编码键(也称为动态键)的 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)

0
投票

因此,如果上面和下面的实现没有区别,我不将属性更改为自定义名称?

是的,但是这里有一点误解。您拥有的两个实现“字面上”是相同的,因为在第二个实现中,永远不会使用 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
    }
}

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


还有其他情况需要使用CodingKeys吗?

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


0
投票

通过这个例子,我将带来所有 3 个示例,以便您在需要时可以理解

CodingKey

假设您的 

Json

回复如下:

{
    Firstname: "Tom"
    Lastname: "Cruise"
    age: 34
}

您的结构模型如下:

struct Person: Codable { let firstName: String let lastName: String let age: Int var hobby: String? // You want to set the value manually from some other source other than the `json` data }

用例

您想保留属性名称
    age
  1. 原样。
    您想要更改 
  2. Firstname
  3. ->
    firstName
    Lastname
    ->
    lastName
    您不想从 json 中获取 
  4. hobby
  5. ,您想手动设置/更新其他来源的值。
    
    
  6. 为了满足您的这 3 种情况,您将添加编码键,如下所示:

struct Person: Codable { let firstName: String let lastName: String let age: Int var hobby: String? // Optional hobby which will be ignored during decoding // Here's your answer: enum CodingKeys: String, CodingKey { case firstName = "Firstname" case lastName = "Lastname" case age //case hobby // It will be ignored during decoding } }

因此 
如果您有用例 2 和/或 3,那么您需要

CodingKey。如果您只有用例 1,则不需要

CodingKey

© www.soinside.com 2019 - 2024. All rights reserved.