Swift: 正确解码不精确的小数
Swift: Decode imprecise decimal correctly
我需要正确解码(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
}
注意:我无法更改要解码的接收内容,我一直在使用十进制数。
您可以实现自己的解码方法,将双精度转换为字符串并使用它来初始化十进制属性:
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
我需要正确解码(Decodable
协议)一个不精确的十进制值,来自
如果尝试将任何数字初始化为字符串
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
}
注意:我无法更改要解码的接收内容,我一直在使用十进制数。
您可以实现自己的解码方法,将双精度转换为字符串并使用它来初始化十进制属性:
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