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

Swift中解码含可变键的JSON:如何定义Codable的Response结构体?

解决Swift中Codable解析动态键顶层JSON的问题

首先,我们先定义对应单个条目的Item结构体,它完美匹配JSON里每个子对象的结构:

struct Item: Codable {
    let id: String
    let title: String
}

接下来是核心的Response结构体。你的JSON顶层是动态字符串键(唯一ID)对应Item对象的字典,常规固定属性的Codable结构体没法适配这种结构,所以我们通过自定义Codable实现,把顶层字典封装到结构体里,同时还能添加方便的访问方式:

struct Response: Codable {
    // 存储底层的ID-Item映射字典
    private let items: [String: Item]
    
    // 计算属性:快速获取所有Item的数组(方便遍历操作)
    var allItems: [Item] {
        Array(items.values)
    }
    
    // 下标方法:通过ID直接查找对应的Item,用法更直观
    subscript(itemId: String) -> Item? {
        items[itemId]
    }
    
    // 自定义解码逻辑:直接把顶层完整字典解码到items属性
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        items = try container.decode([String: Item].self)
    }
    
    // 如果需要把Response再编码回JSON,实现对应的编码逻辑(可选,按需添加)
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(items)
    }
}

解码使用示例

完全按照你要求的方式调用JSONDecoder.decode即可,这里给个完整的测试示例:

// 模拟从API获取的JSON数据(实际场景中替换为真实请求返回的data)
let sampleJsonData = """
{ "2546": { "id": "2546", "title": "Divis and the Black Mountain" }, "1": { "id": "1", "title": "A la Ronde" }, "2": { "id": "2", "title": "Aberconwy House" } }
""".data(using: .utf8)!

do {
    let response = try JSONDecoder().decode(Response.self, from: sampleJsonData)
    
    // 输出所有条目
    print("所有条目:\(response.allItems)")
    
    // 通过ID快速查找特定条目
    if let targetItem = response["2546"] {
        print("ID为2546的条目标题:\(targetItem.title)")
    }
} catch {
    print("解码出错:\(error.localizedDescription)")
}

为什么这么实现?

因为你的JSON顶层没有固定的键名(全是动态生成的唯一ID),常规用CodingKeys绑定固定属性的方式完全不适用。通过singleValueContainer,我们直接把整个顶层字典解码到结构体的内部属性中,既满足了你要求的Response结构体类型,又能完美适配这种动态键的JSON结构。

如果不需要额外的便捷访问方法,你也可以简化Response结构体,只保留核心的编解码逻辑:

struct Response: Codable {
    // 也可以把items设为public,直接暴露字典给外部使用
    public let items: [String: Item]
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        items = try container.decode([String: Item].self)
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(items)
    }
}

内容的提问来源于stack exchange,提问作者Vince O'Sullivan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 14:02:42