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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 17:48:04