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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 23:45:35