Swift如何用单结构体实现指定CodingKeys的Decodable按需解码
解决方案
完全可以实现单结构体按需解码,这里提供两种常用的落地方案,你可以根据业务场景选择:
方案一:泛型封装(编译期类型安全,推荐大部分场景使用)
仅需定义一次Root结构,不同的Info字段模型遵循统一协议即可,不需要重复编写Root相关的重复代码:
// 统一约束Info类模型的协议 protocol InfoDecodable: Decodable {} // 不同场景的Info模型 struct InfoText: InfoDecodable { var text: String } struct InfoNum: InfoDecodable { var num: Int } struct InfoFull: InfoDecodable { var text: String; var num: Int } // 仅需定义一次的通用Root结构体 struct Root<T: InfoDecodable>: Decodable { let info: T }
使用时直接指定泛型类型即可,编译期就能保证你需要的字段必然存在,不会有可选值兜底的问题:
- 仅解码text:
try JSONDecoder().decode(Root<InfoText>.self, from: jsonData) - 仅解码num:
try JSONDecoder().decode(Root<InfoNum>.self, from: jsonData) - 解码全部字段:
try JSONDecoder().decode(Root<InfoFull>.self, from: jsonData)
方案二:动态CodingKeys(极致灵活,适合字段动态变化的场景)
完全只使用一套Root+Info结构体,通过解码器的用户配置传入需要解码的字段集合,不需要新增任何额外模型:
- 首先实现动态CodingKey和自定义解码配置key:
// 支持动态传入字段名的CodingKey struct DynamicCodingKey: CodingKey { let stringValue: String let intValue: Int? init?(stringValue: String) { self.stringValue = stringValue self.intValue = nil } init?(intValue: Int) { self.intValue = intValue self.stringValue = "\(intValue)" } } // 解码器自定义配置的key extension JSONDecoder { static let requiredDecodeFields = CodingUserInfoKey(rawValue: "requiredDecodeFields")! }
- 实现自定义解码逻辑的Info和Root结构体:
struct Info: Decodable { var text: String? var num: Int? init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: DynamicCodingKey.self) // 未指定解码字段时默认解码所有字段 let targetFields = decoder.userInfo[JSONDecoder.requiredDecodeFields] as? [String] ?? ["text", "num"] if targetFields.contains("text") { text = try container.decodeIfPresent(String.self, forKey: DynamicCodingKey(stringValue: "text")!) } if targetFields.contains("num") { num = try container.decodeIfPresent(Int.self, forKey: DynamicCodingKey(stringValue: "num")!) } } } struct Root: Decodable { let info: Info }
- 使用时按需传入要解码的字段即可:
let decoder = JSONDecoder() // 仅解码text字段 decoder.userInfo[JSONDecoder.requiredDecodeFields] = ["text"] let result = try decoder.decode(Root.self, from: jsonData) // 仅解码num字段 decoder.userInfo[JSONDecoder.requiredDecodeFields] = ["num"] let result2 = try decoder.decode(Root.self, from: jsonData) // 解码所有字段 decoder.userInfo.removeValue(forKey: JSONDecoder.requiredDecodeFields) let result3 = try decoder.decode(Root.self, from: jsonData)
方案选型建议
- 如果你对类型安全要求高,解码场景相对固定,优先选择泛型封装方案,没有运行时风险,代码维护成本更低
- 如果你需要解码的字段是运行时动态决定的,场景非常灵活,选择动态CodingKeys方案,不需要新增任何模型类型
内容的提问来源于stack exchange,提问作者iUrii
相关产品推荐
相关产品推荐

