假设我想按如下方式解码 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
}
首先,使用
CodingKeys
有一个成败规则:
如果 JSON(或任何符合
CodingKeys
的格式)键与相应的属性完全匹配(如您的示例中所示),或者转换由适当的 Codable
涵盖,则可以完全省略
keyDecodingStrategy
。
否则你必须指定all
CodingKeys
你需要解码(另见下面的原因#3)。
使用
CodingKeys
的三大理由:
CodingKey
才能解码该密钥。id
常量初始化的 UUID
属性。如果您实现
CodingKeys
来解码 keyed容器,则
init(from decoder
是必需的。
您可以以不同的方式使用 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发生了变化,例如大写或使用不同的单词,您应该使用枚举将正确的键与变量名称映射。
如果您有一个包含任意数量的编码键(也称为动态键)的 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)
因此,如果上面和下面的实现没有区别,我不将属性更改为自定义名称?
是的,但是这里有一点误解。您拥有的两个实现“字面上”是相同的,因为在第二个实现中,永远不会使用 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 不仅仅由 Decodable 使用,它们也由 Encodable 使用。使用 Encodable 时,使用 CodingKeys 的一个原因是指定仅应序列化实例字段的子集。
通过这个例子,我将带来所有 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
Firstname
firstName
和 Lastname
->lastName
。您不想从 json 中获取 hobby
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
。