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

如何适配API动态切换的String/Double类型,避免Decodable解析返回nil?

解答:处理API字段在String和Double之间切换的解码问题

首先直接回答你的核心疑问:仅使用Double?是不可行的。原因很简单:默认的Decodable实现只会尝试匹配JSON中的Double类型值,如果API返回的是字符串(比如"42000.5"),解码器会直接抛出typeMismatch错误,导致整个BitcoinGBP实例解析失败,而不是把price或percentChange24h设为nil。

要解决这个问题,我们需要自定义解码逻辑,让结构体能够同时处理Double和可转换为Double的String类型字段。下面提供两种实用的方案:

方案一:自定义可复用的FlexibleDouble类型

这种方式适合多个结构体都需要处理类似字段的场景,通过封装一个通用的类型来处理两种格式的解码:

struct FlexibleDouble: Decodable {
    let value: Double
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        // 先尝试直接解码为Double
        do {
            value = try container.decode(Double.self)
        } catch {
            // 失败则尝试解码为String再转成Double
            let stringValue = try container.decode(String.self)
            guard let doubleValue = Double(stringValue) else {
                throw DecodingError.dataCorruptedError(
                    in: container,
                    debugDescription: "无法将字符串 \(stringValue) 转换为Double类型"
                )
            }
            value = doubleValue
        }
    }
}

然后修改你的BitcoinGBP结构体,使用这个自定义类型来解码字段:

struct BitcoinGBP : Decodable {
    let price : Double
    let percentChange24h : Double
    
    private enum CodingKeys : String, CodingKey {
        case price = "PRICE"
        case percentChange24h = "CHANGEPCT24HOUR"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 用FlexibleDouble解码后取出实际的Double值
        let priceWrapper = try container.decode(FlexibleDouble.self, forKey: .price)
        price = priceWrapper.value
        
        let changeWrapper = try container.decode(FlexibleDouble.self, forKey: .percentChange24h)
        percentChange24h = changeWrapper.value
    }
}

方案二:直接在结构体的init(from:)中处理字段

如果只有这一个结构体需要处理这种情况,也可以直接在自定义初始化方法中逐个字段处理,省去额外的类型封装:

struct BitcoinGBP : Decodable {
    let price : Double
    let percentChange24h : Double
    
    private enum CodingKeys : String, CodingKey {
        case price = "PRICE"
        case percentChange24h = "CHANGEPCT24HOUR"
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        // 处理price字段
        do {
            price = try container.decode(Double.self, forKey: .price)
        } catch DecodingError.typeMismatch {
            let priceString = try container.decode(String.self, forKey: .price)
            guard let priceDouble = Double(priceString) else {
                throw DecodingError.dataCorruptedError(
                    forKey: .price,
                    in: container,
                    debugDescription: "PRICE字段既不是Double,也不是可转换为Double的String"
                )
            }
            price = priceDouble
        }
        
        // 处理percentChange24h字段
        do {
            percentChange24h = try container.decode(Double.self, forKey: .percentChange24h)
        } catch DecodingError.typeMismatch {
            let changeString = try container.decode(String.self, forKey: .percentChange24h)
            guard let changeDouble = Double(changeString) else {
                throw DecodingError.dataCorruptedError(
                    forKey: .percentChange24h,
                    in: container,
                    debugDescription: "CHANGEPCT24HOUR字段既不是Double,也不是可转换为Double的String"
                )
            }
            percentChange24h = changeDouble
        }
    }
}

两种方案都能让你的结构体自动适配API返回的String或Double类型字段,避免解析失败返回nil。如果API返回的字符串无法转换为Double(比如乱码或非数字字符串),解码会抛出明确的错误,你可以根据需求调整错误处理逻辑(比如设置默认值)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:42:49