如何适配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
相关产品推荐
相关产品推荐

