Swift: правильно расшифровать неточный десятичный - PullRequest
0 голосов
/ 13 марта 2019

Мне нужно правильно декодировать (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
}

Примечание: я не могу изменить то, что принимается для декодирования, я застрял, работая с десятичными числами.

1 Ответ

0 голосов
/ 13 марта 2019

Вы можете реализовать свой собственный метод декодирования и отформатировать ваш дубль максимум с двумя дробными цифрами и использовать строку для инициализации ваших десятичных свойств:

struct Root: Codable {
    let priceAfterTax, priceBeforeTax, tax, taxAmount: Decimal
}

extension Root {
    public init(from decoder: Decoder) throws {
        var container = try decoder.container(keyedBy: CodingKeys.self)

        let priceAfterTaxDouble = try container.decode(Double.self, forKey: .priceAfterTax)
        priceAfterTax = Decimal(string: Formatter.decimal.string(for: priceAfterTaxDouble)!) ?? 0

        let priceBeforeTaxDouble = try container.decode(Double.self, forKey: .priceBeforeTax)
        priceBeforeTax = Decimal(string: Formatter.decimal.string(for: priceBeforeTaxDouble)!) ?? 0

        let taxDouble = try container.decode(Double.self, forKey: .tax)
        tax = Decimal(string: Formatter.decimal.string(for: taxDouble)!) ?? 0

        let taxAmountDouble = try container.decode(Double.self, forKey: .taxAmount)
        taxAmount = Decimal(string: Formatter.decimal.string(for: taxAmountDouble)!) ?? 0
    }
}

extension Formatter {
    static let decimal: NumberFormatter = {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        formatter.decimalSeparator = "."
        formatter.maximumFractionDigits = 2
        return formatter
    }()
}

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
...