Swift:正确解码不精确的十进制
Posted
技术标签:
【中文标题】Swift:正确解码不精确的十进制【英文标题】:Swift: Decode imprecise decimal correctly 【发布时间】:2019-03-12 22:10:25 【问题描述】:我需要从this question 正确解码(Decodable
协议)一个不精确的十进制值我了解如何正确处理 Decimal 实例化,但是在解码时我该如何做到这一点?
如果尝试将任何数字作为字符串初始化
if let value = try! container.decode(String.self, forKey: .d)
self.taxAmount = Decimal(string: value)
我收到Fatal Error: "Expected to decode String but found a number instead."
如果尝试将 130.43 初始化为十进制
if let value = try! container.decode(Decimal.self, forKey: .d)
//value.description is 130.43000000000002048
self.d = Decimal(string: value.description)
//making subtotal to be also 130.43000000000002048 and not 130.43
有没有办法在解码时使用这两个构造函数?
NSDecimalNumber(string: "1.66")
NSDecimalNumber(value: 166).dividing(by: 100)
Decimal(166)/Decimal(100)
Decimal(sign: .plus, exponent: -2, significand: 166)
这是我从外部服务收到的 JSON 的简化版本:
"priceAfterTax": 150.00,
"priceBeforeTax": 130.43,
"tax": 15.00,
"taxAmount": 19.57
注意:我无法更改接收到的要解码的内容,我一直在使用十进制数字。
【问题讨论】:
这实际上是一个大问题,在大多数语言中都没有正确处理。通常的解决方案是解析Double
,将其存储到Decimal
,然后将round
存储到给定数量的Decimal
数字中。
很遗憾你不能。 JSONDecoder
在内部使用NSJSONSerialization
,解码为Double
。因此,即使您解码 Decimal
,它也会首先在内部解码为 Double
,因此会丢失精度。正如 Sulthan 指出的那样,有一种解决方法,但由于此实施问题,没有真正的解决方案。
您可以简单地将其编码和解码为 String 并创建一个返回 Decimal 的计算属性
嗨@LeoDabus 我不能,我总是收到一个号码,如果我可以编辑收到的数据相信我,那将是我的第一次尝试。
您可以使用数字格式化程序将接收到的数据转换为字符串
【参考方案1】:
您可以实现自己的解码方法,将您的双精度转换为字符串并使用它来初始化您的十进制属性:
extension LosslessStringConvertible
var string: String .init(self)
extension FloatingPoint where Self: LosslessStringConvertible
var decimal: Decimal? Decimal(string: string)
struct Root: Codable
let priceAfterTax, priceBeforeTax, tax, taxAmount: Decimal
extension Root
public init(from decoder: Decoder) throws
let container = try decoder.container(keyedBy: CodingKeys.self)
self.priceAfterTax = try container.decode(Double.self, forKey: .priceAfterTax).decimal ?? .zero
self.priceBeforeTax = try container.decode(Double.self, forKey: .priceBeforeTax).decimal ?? .zero
self.tax = try container.decode(Double.self, forKey: .tax).decimal ?? .zero
self.taxAmount = try container.decode(Double.self, forKey: .taxAmount).decimal ?? .zero
let data = Data("""
"priceAfterTax": 150.00,
"priceBeforeTax": 130.43,
"tax": 15.00,
"taxAmount": 19.57
""".utf8)
let decodedObj = try! JSONDecoder().decode(Root.self, from: data)
decodedObj.priceAfterTax // 150.00
decodedObj.priceBeforeTax // 130.43
decodedObj.tax // 15.00
decodedObj.taxAmount // 19.57
【讨论】:
以上是关于Swift:正确解码不精确的十进制的主要内容,如果未能解决你的问题,请参考以下文章