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

iOS 10中Swift 4 JSONDecoder解码大Int64报错的解决方法咨询

解决iOS 10中Swift 4 JSONDecoder解码超大64位整数的问题

我之前维护iOS 10兼容项目时刚好碰到过一模一样的问题——iOS 11及以上系统因为JSONDecoder的实现优化,不会出现这个报错,但iOS 10里确实会因为超大64位整数超出Int64范围抛出异常。既然不能修改JSON结构,我们可以通过自定义Decodable实现绕过这个限制,下面是两个可行的方案:

方案一:封装NSDecimalNumber为可解码类型

由于NSDecimalNumber本身没有实现Decodable协议,我们可以创建一个包装结构体,手动实现解码逻辑来处理超大数字:

import Foundation

struct DecimalValue: Decodable {
    let rawValue: NSDecimalNumber
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        // 先尝试常规解码,捕获Int64溢出错误
        do {
            let intValue = try container.decode(Int64.self)
            rawValue = NSDecimalNumber(value: intValue)
        } catch DecodingError.dataCorrupted(let context) where context.debugDescription.contains("does not fit in Int64") {
            // 捕获到溢出错误后,直接解析原始JSON对象
            guard let jsonNumber = try container.decode(Any.self) as? NSNumber else {
                throw DecodingError.dataCorruptedError(
                    in: container,
                    debugDescription: "无法将值转换为NSDecimalNumber"
                )
            }
            rawValue = NSDecimalNumber(decimal: jsonNumber.decimalValue)
        } catch {
            // 处理其他解码错误
            throw DecodingError.dataCorruptedError(
                in: container,
                debugDescription: "无效的数值格式: \(error.localizedDescription)"
            )
        }
    }
}

然后在你的数据模型中使用这个包装类型:

struct UserData: Decodable {
    let id: DecimalValue
    let timestamp: DecimalValue
    // 其他字段...
}

方案二:扩展KeyedDecodingContainer直接解码NSDecimalNumber

如果你不想额外定义包装类型,可以扩展KeyedDecodingContainer,添加一个专门解码超大数字的方法:

import Foundation

extension KeyedDecodingContainer {
    func decodeDecimalNumber(forKey key: Key) throws -> NSDecimalNumber {
        let singleContainer = try nestedSingleValueContainer(forKey: key)
        
        do {
            // 先尝试解析为Int64
            let intValue = try singleContainer.decode(Int64.self)
            return NSDecimalNumber(value: intValue)
        } catch DecodingError.dataCorrupted(let context) where context.debugDescription.contains("does not fit in Int64") {
            // 处理超大数字
            guard let jsonNumber = try singleContainer.decode(Any.self) as? NSNumber else {
                throw DecodingError.dataCorruptedError(
                    forKey: key,
                    in: self,
                    debugDescription: "字段\(key.stringValue)不是有效的数值类型"
                )
            }
            return NSDecimalNumber(decimal: jsonNumber.decimalValue)
        } catch {
            throw DecodingError.dataCorruptedError(
                forKey: key,
                in: self,
                debugDescription: "解码\(key.stringValue)失败: \(error.localizedDescription)"
            )
        }
    }
}

使用时在模型的init(from:)方法中调用这个扩展方法:

struct OrderInfo: Decodable {
    let orderId: NSDecimalNumber
    let amount: NSDecimalNumber
    
    enum CodingKeys: String, CodingKey {
        case orderId, amount
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        orderId = try container.decodeDecimalNumber(forKey: .orderId)
        amount = try container.decodeDecimalNumber(forKey: .amount)
    }
}

方案说明

这两个方案的核心逻辑都是:

  1. 先尝试用常规方式解码Int64,如果成功就直接转为NSDecimalNumber
  2. 当捕获到does not fit in Int64的错误时,直接从JSON原始对象中获取NSNumber,再转为NSDecimalNumber(NSNumber可以处理超出Int64范围的数值,不会丢失精度)

这样既保留了Codable的便捷性,又解决了iOS 10的兼容性问题,而且不需要修改后端的JSON结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:04:20