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) } }
方案说明
这两个方案的核心逻辑都是:
- 先尝试用常规方式解码
Int64,如果成功就直接转为NSDecimalNumber - 当捕获到
does not fit in Int64的错误时,直接从JSON原始对象中获取NSNumber,再转为NSDecimalNumber(NSNumber可以处理超出Int64范围的数值,不会丢失精度)
这样既保留了Codable的便捷性,又解决了iOS 10的兼容性问题,而且不需要修改后端的JSON结构。
内容的提问来源于stack exchange,提问作者shelll
相关产品推荐
相关产品推荐

