Swift中JSON的Double值解码为CGFloat失败的解决方法
解决Swift解码JSON中数值/字符串混合字段的问题
问题分析
核心问题是JSON数据集中的scaleFactor字段类型不一致:部分条目是数值类型(如1.1),但空字段或异常值被存储为字符串类型(如""),而Swift的JSONDecoder无法自动处理这种类型混合的解码场景,直接用CGFloat.self解码会触发崩溃。
解决方案
方案1:修改JSON数据集(推荐,逻辑更简洁)
直接统一JSON中scaleFactor字段的类型:
- 将所有空值替换为
null或默认数值(如0.5) - 确保所有数值类型的
scaleFactor保持为纯数值格式(不要用引号包裹)
修改后,可直接恢复原解码代码:
scaleFactor = try container.decode(CGFloat.self, forKey: .scaleFactor)
方案2:在Swift代码中兼容解析
如果无法修改数据集,可自定义解码逻辑,同时处理数值和字符串两种输入类型:
方法A:直接在init(from:)中处理
替换Monster结构体init(from decoder:)中注释的scaleFactor相关代码为:
// 处理scaleFactor的兼容解码 do { // 优先尝试解码为Double(JSON数值类型) let doubleScale = try container.decode(Double.self, forKey: .scaleFactor) scaleFactor = CGFloat(doubleScale) } catch { // 解码失败,尝试解码为String类型 let stringScale = try container.decode(String.self, forKey: .scaleFactor) // 尝试将字符串转为Double,失败则用默认值0.5 scaleFactor = CGFloat(Double(stringScale) ?? 0.5) }
方法B:自定义可解码包装类型(更优雅,可复用)
创建通用包装类型,统一处理数值与字符串的兼容解析:
struct FlexibleCGFloat: Codable { let value: CGFloat init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() // 优先尝试解码为数值类型 if let doubleValue = try? container.decode(Double.self) { value = CGFloat(doubleValue) } // 尝试解码为字符串并转换 else if let stringValue = try? container.decode(String.self) { value = CGFloat(Double(stringValue) ?? 0.5) } // 都失败则使用默认值(或抛出错误) else { value = 0.5 // 若需严格校验,可抛出错误: // throw DecodingError.dataCorruptedError(in: container, debugDescription: "scaleFactor格式无效,需为数值或可转换的字符串") } } }
随后在Monster的init(from:)中使用该包装类型:
let flexibleScale = try container.decode(FlexibleCGFloat.self, forKey: .scaleFactor) scaleFactor = flexibleScale.value
额外检查
确认CodingKeys枚举已包含scaleFactor,从提供的代码看该字段已存在,无需修改。
内容的提问来源于stack exchange,提问作者Nathanael Tse
相关产品推荐
相关产品推荐

