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

标签 swift decodable nsdecimalnumber

我需要正确解码(Decodable 协议(protocol))一个不精确的十进制值,来自 this question我了解如何正确处理 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 转换为字符串并使用它来初始化您的十进制属性:

    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:正确解码不精确的十进制,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55131400/

    相关文章:

    swift - 类型 'DBTweet' 不符合协议(protocol) 'Decodable'

    ios - 如果字典数组缺少一个键,如何编写 Codable

    ios - 获取小数点后两位的 NSDecimalNumber

    ios - 将货币字符串转换为 NSDecimalNumber

    swift - 在 Swift 应用程序上使用 NSNetService 和 NSNetServiceBrowser

    swift - "Declaring a public let for an internal class"- swift

    json - 在 Swift 4 中使用 Decodable 解析 JSON

    xcode - 如何快速舍入 NSDecimalNumber?

    ios - Alamofire POST 请求,正文中有一个简单的字符串作为参数, header 仍然显示缺少的参数值

    ios - 如果数据类型为 URL 的变量为空,如何在 Swift 中检查?