如何在Codable类型中使用Any?解决多类型ID编码兼容问题
嘿,这个问题我之前踩过类似的坑!确实,Any类型因为没有遵循Codable协议,直接放在Codable结构体里肯定会报错。不过别担心,有几种优雅的解决办法,我给你梳理一下:
方案1:使用带关联值的枚举(最推荐,类型安全)
这种方法是把可能的ID类型封装成一个枚举,让枚举自己实现Codable,这样既明确了支持的类型,又能保证类型安全,不会出现Any带来的不确定性。
代码示例:
enum IDType: Codable { case string(String) case integer(Int) // 自定义解码逻辑:尝试先转成String,不行再转Int init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let strValue = try? container.decode(String.self) { self = .string(strValue) } else if let intValue = try? container.decode(Int.self) { self = .integer(intValue) } else { throw DecodingError.typeMismatch( IDType.self, DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "ID只能是String或Int类型" ) ) } } // 自定义编码逻辑:根据枚举值的类型编码 func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch self { case .string(let str): try container.encode(str) case .integer(let int): try container.encode(int) } } } // 现在Person结构体的id用IDType类型 struct Person: Codable { var id: IDType }
使用的时候也很方便,比如判断ID类型:
let person = try JSONDecoder().decode(Person.self, from: jsonData) switch person.id { case .string(let strId): print("字符串ID:\(strId)") case .integer(let intId): print("整数ID:\(intId)") }
方案2:直接在Person里自定义Codable实现
如果你不想额外定义枚举,也可以直接在Person结构体里重写解码和编码方法,直接处理Any类型的id:
代码示例:
struct Person: Codable { var id: Any // 自定义解码 init(from decoder: Decoder) throws { let keyedContainer = try decoder.container(keyedBy: CodingKeys.self) let idContainer = try keyedContainer.singleValueContainer(forKey: .id) // 尝试解码成Int,失败再试String if let intId = try? idContainer.decode(Int.self) { id = intId } else if let strId = try? idContainer.decode(String.self) { id = strId } else { throw DecodingError.typeMismatch( Any.self, DecodingError.Context( codingPath: [CodingKeys.id], debugDescription: "ID只能是String或Int类型" ) ) } } // 自定义编码 func encode(to encoder: Encoder) throws { var keyedContainer = encoder.container(keyedBy: CodingKeys.self) switch id { case let intId as Int: try keyedContainer.encode(intId, forKey: .id) case let strId as String: try keyedContainer.encode(strId, forKey: .id) default: throw EncodingError.invalidValue( id, EncodingError.Context( codingPath: [CodingKeys.id], debugDescription: "ID只能是String或Int类型" ) ) } } // 定义编码解码的key enum CodingKeys: String, CodingKey { case id } }
不过这个方案的缺点是id还是Any类型,后续使用的时候需要强制类型转换,容易出现类型错误,所以更推荐第一种方案。
小提示
如果之后ID可能支持更多类型(比如UUID),只需要扩展IDType枚举的case,然后更新解码和编码逻辑就行,扩展性很强。
内容的提问来源于stack exchange,提问作者PGDev
相关产品推荐
相关产品推荐

