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

Swift 4中JSONDecoder解析动态键名JSON的实现方法

刚好我之前处理过类似的场景,CoinmarketCap这种根据用户选择动态返回不同price_xxx键的JSON,确实需要灵活点的解码方式,下面给你两种实用的方案,你可以根据需求选:

方案一:提前枚举已知货币键(类型安全)

如果你的应用只支持有限几种货币(比如欧元、英镑、美元),这种方案最稳妥,类型安全而且容易维护:

struct CryptoPrice: Decodable {
    let price: Double
    let currency: String
    
    // 提前把支持的货币键都列在这里
    private enum CodingKeys: String, CodingKey {
        case priceEur = "price_eur"
        case priceGbp = "price_gbp"
        case priceUsd = "price_usd"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 依次检查哪个键存在,解析对应的值和货币类型
        if let eurPriceStr = try container.decodeIfPresent(String.self, forKey: .priceEur),
           let priceVal = Double(eurPriceStr) {
            price = priceVal
            currency = "EUR"
        } else if let gbpPriceStr = try container.decodeIfPresent(String.self, forKey: .priceGbp),
                  let priceVal = Double(gbpPriceStr) {
            price = priceVal
            currency = "GBP"
        } else if let usdPriceStr = try container.decodeIfPresent(String.self, forKey: .priceUsd),
                  let priceVal = Double(usdPriceStr) {
            price = priceVal
            currency = "USD"
        } else {
            throw DecodingError.dataCorruptedError(
                forKey: .priceEur,
                in: container,
                debugDescription: "未找到支持的价格字段"
            )
        }
    }
}

优点:编译时就能检查键名是否正确,不容易出错;缺点:如果要新增支持的货币,需要手动添加对应的CodingKey和判断分支。

方案二:动态匹配price_开头的键(自动适配新货币)

如果你的应用需要支持CoinmarketCap所有的货币,或者不想每次加货币都改代码,那可以用动态CodingKey的方式,自动识别所有price_开头的键:

struct CryptoPrice: Decodable {
    let price: Double
    let currency: String
    
    init(from decoder: Decoder) throws {
        // 用自定义的动态CodingKey容器
        let container = try decoder.container(keyedBy: DynamicCodingKey.self)
        
        // 遍历所有键,找到以price_开头的字段
        for key in container.allKeys {
            guard key.stringValue.starts(with: "price_") else { continue }
            
            // 解析价格字符串并转成Double
            let priceStr = try container.decode(String.self, forKey: key)
            guard let priceVal = Double(priceStr) else {
                throw DecodingError.dataCorruptedError(
                    forKey: key,
                    in: container,
                    debugDescription: "价格格式无效:\(priceStr)"
                )
            }
            
            // 提取货币代码(比如price_eur -> EUR)
            let currencyCode = key.stringValue.replacingOccurrences(of: "price_", with: "").uppercased()
            price = priceVal
            currency = currencyCode
            return // 找到第一个符合的就返回,因为API每次只会返回一个价格键
        }
        
        // 如果遍历完没找到任何price_开头的键,抛出错误
        throw DecodingError.dataCorrupted(DecodingError.Context(
            codingPath: [],
            debugDescription: "响应中未找到价格字段"
        ))
    }
    
    // 自定义动态CodingKey,支持任意字符串键
    private struct DynamicCodingKey: CodingKey {
        let stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }
        
        let intValue: Int?
        init?(intValue: Int) {
            self.intValue = intValue
            self.stringValue = String(intValue)
        }
    }
}

优点:不用提前知道所有货币,自动适配API新增的货币类型;缺点:编译时无法检查键名,需要确保API返回的键格式符合price_xxx的规则。

如何使用

不管用哪种方案,解析数组类型的JSON都很简单:

// 假设你已经拿到了API返回的Data(比如通过URLSession请求)
let jsonData: Data = ...

do {
    let priceList = try JSONDecoder().decode([CryptoPrice].self, from: jsonData)
    if let firstPrice = priceList.first {
        print("当前价格:\(firstPrice.price) \(firstPrice.currency)")
    }
} catch {
    print("解析失败:\(error.localizedDescription)")
}

注意:CoinmarketCap返回的价格是字符串类型,所以我们需要先转成String再转成Double,直接解码成Double可能会失败哦。

内容的提问来源于stack exchange,提问作者Tarvo Mäesepp

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:21:42