Swift使用Codable解码JSON时自定义对象键缺失的处理方法
Swift Codable 解析缺失键的实现方案
问题场景
解析JSON时存在部分可选键可能缺失的情况,完整JSON结构示例如下:
{ "data": "some text", "id": "3213", "title": "title", "description": "description", "customObj1": [{ "id": "2423", "count": 35 // 其余自定义字段 }], "customObj2": [{ "number": "2423", "name": "john" // 其余自定义字段 }], "customObj3": [{ "like": "2423", "Other": 9 // 其余自定义字段 }] }
其中customObj1、customObj2、customObj3三个字段可能不存在于返回结果中,需要基于Codable协议用struct实现兼容解析。原有未完成的模型代码存在两个问题:一是可选字段没有声明为可选类型,二是自定义decoder初始化逻辑未补全。
实现方法
方法1:最简实现(无需手写init方法)
Codable协议默认会对可选类型属性做键缺失兼容:如果键不存在,会自动赋值为nil,不需要手动写判断逻辑,只需要把可能缺失的字段声明为可选类型即可。
修正后的完整模型代码:
// MARK: - Response struct Response: Codable { let data, id, title: String // 遵循Swift小驼峰命名规范,无需大写开头 let description: String // 可能缺失的字段声明为可选数组类型 let customObj1: [CustomObj1]? let customObj2: [CustomObj2]? let customObj3: [CustomObj3]? enum CodingKeys: String, CodingKey { case data, id, title, description case customObj1, customObj2, customObj3 } } // MARK: - CustomObj1 struct CustomObj1: Codable { let id: String let count: Int } // MARK: - CustomObj2 struct CustomObj2: Codable { let number, name: String } // MARK: - CustomObj3 struct CustomObj3: Codable { let like: String let other: Int enum CodingKeys: String, CodingKey { case like case other = "Other" } }
补充:如果希望键不存在时默认返回空数组而不是
nil,可以给属性加默认值[],同时声明为非可选类型,例如let customObj1: [CustomObj1] = [],Codable也会自动处理缺失键的情况。
方法2:自定义init(from decoder:) 实现
如果需要在解码时做额外逻辑处理,可以手动实现初始化方法,核心是用decodeIfPresent方法解析可选字段,补全后的代码如下:
init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) // 必传字段直接用decode解析,键缺失会直接抛出解码错误 data = try values.decode(String.self, forKey: .data) id = try values.decode(String.self, forKey: .id) title = try values.decode(String.self, forKey: .title) description = try values.decode(String.self, forKey: .description) // 可选字段用decodeIfPresent解析,键不存在时自动返回nil customObj1 = try values.decodeIfPresent([CustomObj1].self, forKey: .customObj1) customObj2 = try values.decodeIfPresent([CustomObj2].self, forKey: .customObj2) customObj3 = try values.decodeIfPresent([CustomObj3].self, forKey: .customObj3) // 原有代码里的self.category是笔误,替换成对应字段即可 // 如果需要自定义默认值,也可以在这里处理,例如: // customObj1 = try values.decodeIfPresent([CustomObj1].self, forKey: .customObj1) ?? [] }
如果要手动判断键是否存在再处理,写法如下:
if values.contains(.customObj1) { customObj1 = try values.decode([CustomObj1].self, forKey: .customObj1) } else { customObj1 = nil }
这种写法和decodeIfPresent效果完全一致,更推荐直接用decodeIfPresent,代码更简洁。
注意事项
- 按照Swift命名规范,属性名建议使用小驼峰,原有代码里的
ResponseDescription大写开头不符合规范,直接命名为description即可,因为该名称和JSON键名一致,不需要额外在CodingKeys里做映射。 - 如果希望字段缺失时不返回
nil而是返回空数组,直接给属性设置默认值[],或者在自定义init里用空合取运算符??做兜底即可。 - 不要混用非可选类型和缺失键场景:如果属性声明为非可选的
[CustomObj1],JSON里缺省对应键时Codable会直接抛出解码错误,必须声明为可选或者设置默认值才能兼容缺省情况。
内容的提问来源于stack exchange,提问作者Vineet Rai
相关产品推荐
相关产品推荐

