使用JSONSerialization序列化货币时指定小数位数 [英] Specify number of decimals when serializing currencies with JSONSerialization

查看:650
本文介绍了使用JSONSerialization序列化货币时指定小数位数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

NumberFormatter使在屏幕上显示值时很容易格式化货币:

NumberFormatter makes it quite easy to format currencies when presenting values on screen:

let decimal = Decimal(25.99)
let decimalNumberFormatter = NumberFormatter()
decimalNumberFormatter.numberStyle = .currencyAccounting
let output = decimalNumberFormatter.string(for: decimal)
// output = "$25.99"

上面的代码对于任何DecimalDouble值都适用.小数位数始终与所使用的语言环境相匹配.

The above code works well both for any Decimal or Double values. The amount of decimal digits always matches that of the locale being used.

证明将浮点货币值序列化为JSON并不是一件容易的事.

Turns our that serializing a floating point currency value to JSON is not that trivial.

具有以下序列化方法(请注意解开力):

Having the following serializing method (mind the force unwraps):

func serialize(prices: Any...) {
    let data = try! JSONSerialization.data(withJSONObject: ["value": prices], options: [])
    let string = String(data: data, encoding: .utf8)!
    print(string)
}

然后我们可以用不同的值和类型来调用它.在某些情况下,DoubleDecimalNSDecimalNumber(应从Swift的Decimal桥接)无法正确呈现该值.

We can then call it with different values and types. Double, Decimal and NSDecimalNumber (which should be bridged from Swift's Decimal) fail to properly render the value in some cases.

serialize(prices: 125.99, 16.42, 88.56, 88.57, 0.1 + 0.2)
// {"value":[125.99,16.42,88.56,88.56999999999999,0.3]}

serialize(prices: Decimal(125.99), Decimal(16.42), Decimal(88.56), Decimal(88.57), Decimal(0.1) + Decimal(0.2))
// {"value":[125.98999999999997952,16.420000000000004096,88.56,88.57,0.3]}

serialize(prices: NSDecimalNumber(value: 125.99), NSDecimalNumber(value: 16.42), NSDecimalNumber(value: 88.56), NSDecimalNumber(value: 88.57), NSDecimalNumber(value: 0.1).adding(NSDecimalNumber(value: 0.2)))
// {"value":[125.98999999999997952,16.420000000000004096,88.56,88.57,0.3]}

我不希望将数字序列化为货币(不需要货币符号,整数(5)或单个小数位(0.3)都可以).但是我正在寻找一种解决方案,其中序列化的输出中所包含的位数不超过给定货币允许的小数位数(区域设置).

I'm not looking to serialize numbers as currencies (no need for currency symbol, integers (5) or single decimal position (0.3) are fine). However I'm looking for a solution where the serialized output contains no more than the number of decimals allowed by a given currency (locale).

这是什么,当将浮点值序列化为JSON时,有没有办法限制或指定要使用的小数位数?

This is, is there any way to limit or specify the number of decimals to be used when serializing floating point values to JSON?

更新#1: 经过更多数据类型的测试,令人惊讶的是,似乎FloatFloat32都适用于两位十进制的货币. Float64失败,显示为Double(可能是相同类型的别名).

Update #1: Tested with more data types, surprisingly seems like both Float and Float32 work well for two-decimal currencies. Float64 fails as Double (probably they are an alias of the same type).

serialize(prices: Float(125.99), Float(16.42), Float(88.56), Float(88.57), Float(0.1) + Float(0.2))
// {"value":[125.99,16.42,88.56,88.57,0.3]}

serialize(prices: Float32(125.99), Float32(16.42), Float32(88.56), Float32(88.57), Float32(0.1) + Float32(0.2))
// {"value":[125.99,16.42,88.56,88.57,0.3]}

serialize(prices: Float64(125.99), Float64(16.42), Float64(88.56), Float64(88.57), Float64(0.1) + Float64(0.2))
// {"value":[125.99,16.42,88.56,88.56999999999999,0.3]}

不过,很难知道它们是否在所有情况下都能正常工作. Float80失败,并出现_NSJSONWriter异常.

Hard to know if they work well in all cases, though. Float80 fails to serialize with a _NSJSONWriter exception.

推荐答案

在对此事进行了一些研究之后,一位同事发现,使用NSDecimalNumberHandler舍入指定行为的值可以解决JSON序列化问题.

After doing some research in this matter, a coworker found that rounding the values specifying a behavior using NSDecimalNumberHandler solves the JSON serialization issue.

fileprivate let currencyBehavior = NSDecimalNumberHandler(roundingMode: .bankers, scale: 2, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: true)

extension Decimal {
    var roundedCurrency: Decimal {
        return (self as NSDecimalNumber).rounding(accordingToBehavior: currencyBehavior) as Decimal
    }
}

按照文章中的示例代码,我们将获得所需的输出:

Following the example code from the post, we get the desired output:

serialize(prices: Decimal(125.99).roundedCurrency, Decimal(16.42).roundedCurrency, Decimal(88.56).roundedCurrency, Decimal(88.57).roundedCurrency, (Decimal(0.1) + Decimal(0.2)).roundedCurrency)
// {"value":[125.99,16.42,88.56,88.57,0.3]}

有效!对10000个值(从0.0到99.99)进行测试,没有发现问题.

It works! Ran a test for 10000 values (from 0.0 to 99.99) and found no issues.

如果需要,可以将小数位数调整为当前语言环境的小数位数:

If needed, the scale can be adjusted to the number of decimals from the current locale:

var currencyFormatter = NumberFormatter()
currencyFormatter.numberStyle = .currencyAccounting
let scale = currencyFormatter.maximumFractionDigits
// scale == 2

这篇关于使用JSONSerialization序列化货币时指定小数位数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆