Swift中能否将不同字段名的JSON字符串解码到同一个结构体对象
Swift 原生 Codable 没有和 Gson @SerializedName(alternate:) 完全对等的注解语法,但可以通过自定义解码逻辑实现完全相同的多字段名映射能力,支持两种不同字段名的JSON解码到同一个结构体。
最简适配方案(直接适配你现有的结构体)
你只需要给 Checklist 添加自定义的解码初始化方法,同时支持读取短key和长key即可:
struct Checklist: Hashable, Codable { let id: Int64 var text: String? var checked: Bool enum CodingKeys: String, CodingKey { case id = "i" case text = "t" case checked = "c" } // 定义未优化版的长key枚举 enum UnoptimizedCodingKeys: String, CodingKey { case id, text, checked } init(from decoder: Decoder) throws { let optimizedContainer = try decoder.container(keyedBy: CodingKeys.self) let unoptimizedContainer = try decoder.container(keyedBy: UnoptimizedCodingKeys.self) // 优先读优化后的短key,不存在则读长key id = try optimizedContainer.decodeIfPresent(Int64.self, forKey: .id) ?? unoptimizedContainer.decode(Int64.self, forKey: .id) text = try optimizedContainer.decodeIfPresent(String.self, forKey: .text) ?? unoptimizedContainer.decodeIfPresent(String.self, forKey: .text) checked = try optimizedContainer.decodeIfPresent(Bool.self, forKey: .checked) ?? unoptimizedContainer.decode(Bool.self, forKey: .checked) } func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: Checklist, rhs: Checklist) -> Bool { return lhs.id == rhs.id } }
修改后你提供的两种JSON字符串都可以正常解码。
通用复用方案(适合多结构体有同类需求)
如果你有多个结构体都需要做多key映射,可以扩展 KeyedDecodingContainer 实现通用的多key解码方法,避免重复写判断逻辑:
extension KeyedDecodingContainer { /// 从多个备选key中解码值,只要找到任意一个存在的key就返回对应值 func decode<T: Decodable>(_ type: T.Type, forAlternativeKeys keys: [K]) throws -> T { for key in keys { if let value = try decodeIfPresent(type, forKey: key) { return value } } throw DecodingError.keyNotFound( keys.first!, DecodingError.Context(codingPath: codingPath, debugDescription: "所有备选key \(keys) 均不存在") ) } /// 可选值版本的多key解码 func decodeIfPresent<T: Decodable>(_ type: T.Type, forAlternativeKeys keys: [K]) throws -> T? { for key in keys { if let value = try decodeIfPresent(type, forKey: key) { return value } } return nil } }
使用时把所有可能的key都放到同一个枚举中,直接调用扩展方法即可:
struct Checklist: Hashable, Codable { let id: Int64 var text: String? var checked: Bool // 把所有可能的字段名都放入同一个枚举 enum AllCodingKeys: String, CodingKey { case id, i, text, t, checked, c } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: AllCodingKeys.self) id = try container.decode(Int64.self, forAlternativeKeys: [.i, .id]) text = try container.decodeIfPresent(String.self, forAlternativeKeys: [.t, .text]) checked = try container.decode(Bool.self, forAlternativeKeys: [.c, .checked]) } func hash(into hasher: inout Hasher) { hasher.combine(id) } static func == (lhs: Checklist, rhs: Checklist) -> Bool { return lhs.id == rhs.id } }
内容的提问来源于stack exchange,提问作者Cheok Yan Cheng
相关产品推荐
相关产品推荐

