如何在Swift中使用Codable解码含NaN值的JSON避免崩溃?
解决Swift Codable解码JSON中NaN值的崩溃问题
问题根源
JSON标准(RFC 8259)中并不包含NaN值,这属于JavaScript的扩展写法。Swift原生JSONDecoder默认不支持解析这类非标准值,遇到时会抛出dataCorrupted错误导致崩溃;而null因为你的结构体属性是可选类型(Int?),所以能被正常解析为nil。
解决方案
方案1:自定义结构体解码方法(针对性处理)
修改Fruits结构体,实现自定义的init(from decoder: Decoder)方法,手动处理包含NaN的字段:
struct Fruits: Codable, Identifiable { var id: Int var apples: Int? var oranges: Int? var bananas: Int? var pineapples: Int? var watermelons: Int? var comment: String? enum CodingKeys: String, CodingKey { case id, apples, oranges, bananas, pineapples, watermelons, comment } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) // 解码必填的id字段 id = try container.decode(Int.self, forKey: .id) // 常规可选Int字段直接解码 apples = try container.decodeIfPresent(Int.self, forKey: .apples) bananas = try container.decodeIfPresent(Int.self, forKey: .bananas) pineapples = try container.decodeIfPresent(Int.self, forKey: .pineapples) watermelons = try container.decodeIfPresent(Int.self, forKey: .watermelons) comment = try container.decodeIfPresent(String.self, forKey: .comment) // 专门处理oranges字段:先解析为Double,判断是否为NaN/非有限值 if let orangeDouble = try container.decodeIfPresent(Double.self, forKey: .oranges) { oranges = orangeDouble.isNaN || !orangeDouble.isFinite ? nil : Int(orangeDouble) } else { oranges = nil } } }
方案2:通用SafeInt类型(批量处理多字段)
如果多个字段都可能出现NaN,可以自定义一个兼容NaN的SafeInt类型,替换结构体中的Int?:
struct SafeInt: Codable { let value: Int? init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() do { // 优先尝试直接解析为Int value = try container.decode(Int.self) } catch { // 失败则尝试解析为Double,处理NaN/非有限值 if let doubleVal = try? container.decode(Double.self) { value = doubleVal.isNaN || !doubleVal.isFinite ? nil : Int(doubleVal) } else { // 其他非法情况统一返回nil value = nil } } } } // 修改后的Fruits结构体 struct Fruits: Codable, Identifiable { var id: Int var apples: SafeInt? var oranges: SafeInt? var bananas: SafeInt? var pineapples: SafeInt? var watermelons: SafeInt? var comment: String? // 可选:添加计算属性方便直接获取Int值 var applesInt: Int? { apples?.value } var orangesInt: Int? { oranges?.value } var bananasInt: Int? { bananas?.value } }
方案3:全局配置JSONDecoder(兼容非标准浮点值)
如果JSON中还存在Infinity这类非标准值,可以全局配置JSONDecoder的解码策略,再配合自定义逻辑处理:
let decoder = JSONDecoder() // 允许解析NaN、正负Infinity,映射为对应的Double值 decoder.nonConformingFloatDecodingStrategy = .convertFromString( positiveInfinity: "Infinity", negativeInfinity: "-Infinity", nan: "NaN" ) // 后续使用这个decoder解码即可,配合方案1或方案2的逻辑处理转Int的问题
最佳建议
如果后端可控,优先让后端将NaN替换为null,完全符合JSON标准,无需前端额外处理;若后端无法修改,再选择上述前端方案。
内容的提问来源于stack exchange,提问作者Ihor Makhnyk
相关产品推荐
相关产品推荐

