Swift属性包装器适配基础类型JSON解码问题求助
解决Swift属性包装器同时解码基础类型的两种JSON格式问题
问题场景
需要通过UUIDEmbedded属性包装器同时兼容两种JSON解码场景:
- 场景1:JSON是包含
id和value字段的字典结构(如{"id": "uuid-string", "value": 25}) - 场景2:JSON是直接的目标类型值(如
25)
当前实现的属性包装器在自定义结构体(如Person)上工作正常,但用于基础类型(如Person中的age: Int)时,仅能解码场景1的JSON,解码场景2会抛出类型不匹配错误:
typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "info", intValue: nil), CodingKeys(stringValue: "value", intValue: nil), CodingKeys(stringValue: "age", intValue: nil)], debugDescription: "Expected to decode Dictionary<String, Any> but found number instead.", underlyingError: nil))
解决方案
调整UUIDEmbedded的解码逻辑,优先尝试直接解码目标类型,失败后再尝试字典结构解码,同时扩展KeyedDecodingContainer确保兼容性:
@propertyWrapper struct UUIDEmbedded<Value: Codable>: Codable { var wrappedValue: Value init(wrappedValue: Value) { self.wrappedValue = wrappedValue } enum CodingKeys: String, CodingKey { case id case value } init(from decoder: Decoder) throws { do { // 优先尝试直接解码目标类型(适配基础类型的直接值场景) self.wrappedValue = try decoder.singleValueContainer().decode(Value.self) } catch { // 直接解码失败,切换为字典结构解码 let container = try decoder.container(keyedBy: CodingKeys.self) self.wrappedValue = try container.decode(Value.self, forKey: .value) // 若需保存id,可在此处添加逻辑存储 // let id = try container.decode(UUID.self, forKey: .id) } } func encode(to encoder: Encoder) throws { // 编码逻辑可按需调整,示例默认编码为字典结构 var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(wrappedValue, forKey: .value) // 若存在id,需同步编码 // try container.encode(id, forKey: .id) } } // 扩展容器,确保直接值场景下能正确解码为UUIDEmbedded类型 extension KeyedDecodingContainer { func decode<T>(_ type: UUIDEmbedded<T>.Type, forKey key: Key) throws -> UUIDEmbedded<T> where T: Codable { return try decodeIfPresent(type, forKey: key) ?? UUIDEmbedded(wrappedValue: try decode(T.self, forKey: key)) } }
测试验证
测试JSON1(字典结构)
{ "info": { "id": "123e4567-e89b-12d3-a456-426614174000", "value": { "name": "Alice", "age": { "id": "987e6543-e21b-43c2-d123-456789012345", "value": 30 }, "job": { "title": "Engineer", "years": 5 } } } }
测试JSON2(直接值结构)
{ "info": { "name": "Bob", "age": 28, "job": { "title": "Designer", "years": 3 } } }
解码测试代码
let json1Data = """ { "info": { "id": "123e4567-e89b-12d3-a456-426614174000", "value": { "name": "Alice", "age": { "id": "987e6543-e21b-43c2-d123-456789012345", "value": 30 }, "job": { "title": "Engineer", "years": 5 } } } } """.data(using: .utf8)! let json2Data = """ { "info": { "name": "Bob", "age": 28, "job": { "title": "Designer", "years": 3 } } } """.data(using: .utf8)! do { let employee1 = try JSONDecoder().decode(Employee.self, from: json1Data) print("员工1年龄:\(employee1.info.age)") // 输出:员工1年龄:30 let employee2 = try JSONDecoder().decode(Employee.self, from: json2Data) print("员工2年龄:\(employee2.info.age)") // 输出:员工2年龄:28 } catch { print("解码错误:\(error)") }
内容的提问来源于stack exchange,提问作者Bluebyte
相关产品推荐
相关产品推荐

