Swift:正确解码不精确的十进制

Jos*_*ose 5 nsdecimalnumber swift decodable

我需要Decodable正确解码(协议)一个不精确的十进制值,从这个问题我了解如何正确处理十进制实例化,但是在解码时如何做到这一点?

如果尝试将任何数字初始化为字符串

if let value = try! container.decode(String.self, forKey: .d) {
    self.taxAmount = Decimal(string: value)
}
Run Code Online (Sandbox Code Playgroud)

我明白了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
}
Run Code Online (Sandbox Code Playgroud)

解码时有什么方法可以使用这个构造函数吗?

  • 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
}
Run Code Online (Sandbox Code Playgroud)

注意:我无法更改接收到的要解码的内容,我只能使用十进制数字。

Leo*_*bus 10

您可以实现自己的解码方法,将双精度数转换为字符串并使用它来初始化十进制属性:


extension LosslessStringConvertible {
    var string: String { .init(self) }
}
Run Code Online (Sandbox Code Playgroud)
extension FloatingPoint where Self: LosslessStringConvertible {
    var decimal: Decimal? { Decimal(string: string) }
}
Run Code Online (Sandbox Code Playgroud)
struct Root: Codable {
    let priceAfterTax, priceBeforeTax, tax, taxAmount: Decimal
}
Run Code Online (Sandbox Code Playgroud)
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
    }
}
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)