I need to decode (Decodable
protocol) an imprecise decimal value correctly, from this question I understand how to properly handle the Decimal instantiation, but how can I do this when decoding?
If trying to init any number as a String
if let value = try! container.decode(String.self, forKey: .d) {
self.taxAmount = Decimal(string: value)
}
I get Fatal Error: "Expected to decode String but found a number instead."
And if try to init 130.43 as a Decimal
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
}
Is there any way to use either of this constructors when decoding?
NSDecimalNumber(string: "1.66")
NSDecimalNumber(value: 166).dividing(by: 100)
Decimal(166)/Decimal(100)
Decimal(sign: .plus, exponent: -2, significand: 166)
Here is a simplified version of the JSON I receive from the external service:
{
"priceAfterTax": 150.00,
"priceBeforeTax": 130.43,
"tax": 15.00,
"taxAmount": 19.57
}
Note: I can't change what is being received to be decoded, I'm stuck working with decimal numbers.
You can implement your own decoding method, convert your double to string and use it to initialize your decimal properties:
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