Swift 的默认行为是使用属性名称直接作为键名。这种情况下,Codable 的自动合成功能会尝试使用类或结构体的属性名称与 JSON 或其他格式的数据中的键名进行一一对应。
但在某些场景下,这种默认行为可能无法满足需求,因此需要手动定义 CodingKeys。
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
}
这部分代码定义了一个枚举,用来表示对象的属性名称在编码和解码过程中与外部数据(如 JSON)的键值映射关系。
Swift 的 Codable 系统在编码和解码时会参考这个枚举,将属性名称和外部数据的键进行映射。
如果属性名称和键名相同,可以省略 = “键名”。
{
"id": "12345",
"name": "John Doe"
}
id 会映射到对象的 id 属性。
name 会映射到对象的 name 属性。
不定义 CodingKeys 的情况
默认行为:自动键值映射
如果不定义 CodingKeys,Swift 会直接使用属性的名称作为键。
struct Friend: Codable {
var id: String
var name: String
}
let jsonData = """
{
"id": "001",
"name": "Alice"
}
""".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let friend = try decoder.decode(Friend.self, from: jsonData)
print(friend) // 输出:Friend(id: "001", name: "Alice")
} catch {
print("解码失败:\(error)")
}
属性名称与外部数据(如 JSON)的键名完全一致时,无需自定义解码或编码逻辑。
必须定义 CodingKeys 的情况
当键名不匹配时
如果 JSON 数据中的键名与类或结构体的属性名称不同,必须通过 CodingKeys 手动定义映射关系。
例如:
struct Friend: Codable {
var id: String
var fullName: String
enum CodingKeys: String, CodingKey {
case id
case fullName = "name"
}
}
let jsonData = """
{
"id": "001",
"name": "Alice"
}
""".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let friend = try decoder.decode(Friend.self, from: jsonData)
print(friend) // 输出:Friend(id: "001", fullName: "Alice")
} catch {
print("解码失败:\(error)")
}
当需要忽略某些属性时
如果某些属性不需要参与编码或解码,可以通过 CodingKeys 排除它们。例如:
struct Friend: Codable {
var id: String
var name: String
var isAdmin: Bool // 仅用于内部逻辑,不需要编码或解码
enum CodingKeys: String, CodingKey {
case id
case name
}
}
当需要自定义键的编码或解码方式时
有时可能需要通过 CodingKeys 实现自定义逻辑。例如:
struct Friend: Codable {
var id: String
var name: String
var createdAt: Date
enum CodingKeys: String, CodingKey {
case id
case name
case createdAt = "created_at"
}
}
当无法使用自动合成时
如果你需要自定义 init(from:) 或 encode(to:) 的实现,通常也需要定义 CodingKeys。
不定义 CodingKeys 的局限性
无法处理键名不匹配的情况
如果属性名称与外部数据的键名不同,Swift 会抛出解码错误。例如:
struct Friend: Codable {
var id: String
var fullName: String // JSON 中的键名是 "name"
}
let jsonData = """
{
"id": "001",
"name": "Alice"
}
""".data(using: .utf8)!
do {
let decoder = JSONDecoder()
let friend = try decoder.decode(Friend.self, from: jsonData)
print(friend)
} catch {
print("解码失败:\(error)") // 解码失败,因为键 "name" 无法匹配 "fullName"
}
无法忽略属性
如果某些属性需要被忽略(不参与编码或解码),不定义 CodingKeys 将导致这些属性也被自动处理。
总结
不定义 CodingKeys
当属性名称与外部数据的键名完全一致时,可以省略 CodingKeys。
自动合成的 Codable 实现会直接使用属性名称作为键名。
需要定义 CodingKeys
属性名称与外部数据键名不同。
某些属性需要忽略。
需要自定义编码或解码逻辑。
JSON 数据或其他外部数据格式需要特殊处理(例如日期格式)。
最佳实践
优先尝试使用 Swift 自动合成的 Codable 功能,但当需要更多的灵活性或处理复杂数据映射时,手动定义 CodingKeys 是必要的。