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

使用Swift的JSONDecoder解析含异构类型字典的JSON问题

如何解析值类型随键变化的JSON数组(Swift JSONDecoder)

这种键对应不同值类型的JSON结构确实容易卡壳,不过我们可以通过自定义Codable实现来完美解决——核心思路是用枚举封装不同的地址条目类型,既适配JSON的多变结构,又能满足Encodable协议要求。

步骤1:定义基础Address结构体

先搞定最基础的地址模型,这个是所有地址类型的通用结构:

struct Address: Codable {
    let street: String
    let state: String
}

步骤2:用枚举封装不同的地址条目类型

因为JSON里的addresses数组元素是单键值对,但值的类型可能是单个Address或[Address],我们用枚举来对应这些场景:

enum AddressEntry: Codable {
    // 枚举case对应JSON里的不同键,关联对应的值类型
    case home(Address)
    case work(Address)
    case favorites([Address])
    
    // 定义JSON里的键名
    private enum CodingKeys: String, CodingKey {
        case home, work, favorites
    }
    
    // 自定义解码逻辑
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 依次检查容器里的键,匹配到哪个就初始化对应的枚举值
        if let homeAddr = try container.decodeIfPresent(Address.self, forKey: .home) {
            self = .home(homeAddr)
        } else if let workAddr = try container.decodeIfPresent(Address.self, forKey: .work) {
            self = .work(workAddr)
        } else if let favoriteAddrs = try container.decodeIfPresent([Address].self, forKey: .favorites) {
            self = .favorites(favoriteAddrs)
        } else {
            throw DecodingError.dataCorruptedError(
                forKey: container.allKeys.first!,
                in: container,
                debugDescription: "无法识别的地址条目类型"
            )
        }
    }
    
    // 自定义编码逻辑,保证符合Encodable协议
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        
        switch self {
        case .home(let addr):
            try container.encode(addr, forKey: .home)
        case .work(let addr):
            try container.encode(addr, forKey: .work)
        case .favorites(let addrs):
            try container.encode(addrs, forKey: .favorites)
        }
    }
}

步骤3:定义主结构体UserProfile

现在可以直接用[AddressEntry]来定义addresses属性,完美适配JSON的数组结构:

struct UserProfile: Codable {
    let name: String
    let addresses: [AddressEntry]
}

测试解码逻辑

用你的测试JSON来验证一下:

let jsonString = """
{ "name":"John Smith", "addresses":[ { "home":{ "street":"123 Main St", "state":"CA" } }, { "work":{ "street":"345 Oak St", "state":"CA" } }, { "favorites":[ { "street":"456 Green St.", "state":"CA" }, { "street":"987 Tambor Rd", "state":"CA" } ] } ] }
"""

do {
    let user = try JSONDecoder().decode(UserProfile.self, from: jsonString.data(using: .utf8)!)
    print("用户名:\(user.name)")
    
    for entry in user.addresses {
        switch entry {
        case .home(let addr):
            print("家庭地址:\(addr.street), \(addr.state)")
        case .work(let addr):
            print("工作地址:\(addr.street), \(addr.state)")
        case .favorites(let addrs):
            print("收藏地址:")
            addrs.forEach { print("- \($0.street), \($0.state)") }
        }
    }
} catch {
    print("解码错误:\(error)")
}

这个方案的优势在于:

  • 完全遵循Codable协议,既支持解码也支持编码,解决了你之前遇到的Encodable不兼容问题
  • 枚举的case让不同类型的地址条目处理逻辑更清晰,后续扩展新的地址类型(比如school)只需要加一个枚举case即可

内容的提问来源于stack exchange,提问作者user1118374

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.11 07:23:30