何时在 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
的三个主要原因:
- A Swift variable/property 名称不得以数字开头。如果密钥确实以数字开头,则必须指定兼容的
CodingKey
才能完全解码密钥。
- 您想使用不同的 属性 名称。
- 你想排除密钥被解码,例如
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)
假设我想解码一个 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
的三个主要原因:
- A Swift variable/property 名称不得以数字开头。如果密钥确实以数字开头,则必须指定兼容的
CodingKey
才能完全解码密钥。 - 您想使用不同的 属性 名称。
- 你想排除密钥被解码,例如
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)