You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:35:01