Swift Codable 怎么用?JSON 键名不一致和默认值怎么处理?
Codable 是 Encodable + Decodable 的类型别名,让 Swift 类型可以自动编解码为 JSON、Plist 等格式。只要所有属性都是 Codable 类型,编译器自动合成编解码逻辑——不需要手写一行解析代码。
swiftstruct User: Codable { let id: Int let name: String let email: String? } // 自动支持 JSONEncoder/JSONDecoder
Codable 最大的价值是消灭了手动 JSON 解析的样板代码。之前用 Objective-C 的 NSJSONSerialization 返回 NSDictionary,再手动取值转型,又长又容易崩溃。Codable 让这个过程类型安全、编译器检查。
追问
JSON 的键名和 Swift 属性名不一样怎么办?
用 CodingKeys 枚举映射:
swiftstruct User: Codable { let name: String enum CodingKeys: String, CodingKey { case name = "full_name" } }
CodingKeys 必须覆盖所有需要编解码的属性——没列出的属性会被跳过(如果是可选型且有默认值)。这个枚举也可以用来"只编码部分字段",不需要的字段不写进 CodingKeys 即可。
什么时候需要自定义 init(from:) 和 encode(to:)?
自动合成搞不定的场景:日期格式(JSON 里是时间戳或字符串,Swift 是 Date)、嵌套结构不一致、默认值逻辑、多个字段组合解码。比如 JSON 里日期是秒级时间戳:
swiftinit(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) let timestamp = try container.decode(Double.self, forKey: .createdAt) self.createdAt = Date(timeIntervalSince1970: timestamp) }
能用 CodingKeys 解决的就不要自定义 init(from:)——自定义越多,维护成本越高。
Codable 的默认值怎么处理?
Swift 不支持在 Codable 属性上直接设默认值——如果 JSON 里缺了某个键,解码直接失败。解法一:用可选型 let email: String?,缺失时为 nil。解法二:自定义 init(from:),用 decodeIfPresent + 空合并运算符:self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? "unknown"。Swift 5.9 有社区提案支持默认值,但目前还没有正式语法。
Codable 和 NSCoding 有什么区别?
NSCoding 是 Objective-C 时代的协议,需要手动实现 encode/decode,类型不安全(Any 类型),只支持 class。Codable 是 Swift 原生的,编译器自动合成,类型安全,struct 和 class 都支持,且不依赖 Objective-C 运行时。新项目用 Codable,没有理由再用 NSCoding。
CustomStringConvertible 和 Codable 有什么关系?
没有直接关系。CustomStringConvertible 控制 print() 和字符串插值的输出格式,Codable 控制序列化/反序列化格式。一个常见错误:在 CustomStringConvertible 的 description 里输出 JSON——应该用 JSONEncoder 编码,description 应该输出人类可读的调试信息,不是 JSON 字符串。
写段代码
swiftstruct User: Codable { let id: Int let name: String let email: String? // 键名映射 enum CodingKeys: String, CodingKey { case id, name case email = "email_address" } } // 编码 let user = User(id: 1, name: "Alice", email: "a@b.com") let data = try JSONEncoder().encode(user) // 解码 let decoded = try JSONDecoder().decode(User.self, from: data) // 自定义日期格式 let decoder = JSONDecoder() decoder.dateDecodingStrategy = .secondsSince1970 decoder.keyDecodingStrategy = .convertFromSnakeCase // snake_case → camelCase