服务端阅读 05月27日 11:30
Swift Codable 怎么用?JSON 键名不一致和默认值怎么处理?
Codable 是 Encodable + Decodable 的类型别名,让 Swift 类型可以自动编解码为 JSON、Plist 等格式。只要所有属性都是 Codable 类型,编译器自动合成编解码逻辑——不需要手写一行解析代码。struct User: Codable { let id: Int let name: String let email: String?}// 自动支持 JSONEncoder/JSONDecoderCodable 最大的价值是消灭了手动 JSON 解析的样板代码。之前用 Objective-C 的 NSJSONSerialization 返回 NSDictionary,再手动取值转型,又长又容易崩溃。Codable 让这个过程类型安全、编译器检查。追问JSON 的键名和 Swift 属性名不一样怎么办?用 CodingKeys 枚举映射:struct User: Codable { let name: String enum CodingKeys: String, CodingKey { case name = "full_name" }}CodingKeys 必须覆盖所有需要编解码的属性——没列出的属性会被跳过(如果是可选型且有默认值)。这个枚举也可以用来"只编码部分字段",不需要的字段不写进 CodingKeys 即可。什么时候需要自定义 init(from:) 和 encode(to:)?自动合成搞不定的场景:日期格式(JSON 里是时间戳或字符串,Swift 是 Date)、嵌套结构不一致、默认值逻辑、多个字段组合解码。比如 JSON 里日期是秒级时间戳:init(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 字符串。写段代码struct 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 = .secondsSince1970decoder.keyDecodingStrategy = .convertFromSnakeCase // snake_case → camelCase