JSON中格式化货币值的标准是什么?

36
考虑到数据类型的各种怪异性和本地化,一个Web服务如何在应用程序之间传递货币价值是最好的方式?是否有某个标准? 我的第一想法是简单地使用数字类型。例如:
"amount": 1234.56

我看到很多争论关于使用浮点数数据类型进行货币计算时缺乏精度和舍入误差的问题-然而,我们只是传输值,不是计算,所以这并不重要。

EventBrite的JSON货币规范类似于这样指定:

{
"currency": "USD", 
"value": 432, 
"display": "$4.32"
}

感谢您避免使用浮点数,但现在我们遇到了另一个问题:我们能够容纳的最大数字是多少?

一条评论(我不确定它是否正确,但似乎有道理)声称,在JSON中数字的实现因而最好你可以期望的是32位带符号整数。一个32位带符号整数可以容纳的最大值是2147483647。如果我们以次要单位表示价值,那就是21,474,836.47美元。2100万美元看起来像一个巨大的数字,但并非不能想象某些应用程序可能需要处理更大的价值。当货币的主要单位由1000个较小单位组成,或者该货币价值低于美元时,问题会变得更加严重。例如,突尼斯第纳尔分为1,000毫升。 2147483647毫升,或2147483.647 TND相当于$1,124,492.04人民币。在某些情况下,可能会使用超过100万美元的价值更加可能。另一个例子:越南盾的次货币已经被通货膨胀摧毁,所以让我们只使用主要单位。 2147483647 VND 相当于 $98,526.55。我相信许多用途(银行余额、房地产价值等)远高于此。(尽管EventBrite可能不必担心门票价格那么高!)

如果我们通过将价值以字符串形式传达来避免这个问题,应该如何格式化字符串?不同的国家/地区有着截然不同的格式——不同的货币符号,符号出现在金额之前或之后,符号和金额之间是否有空格,用逗号还是点来分隔小数,是否使用逗号作为千位分隔符,括号或负号表示负值,可能还有我不知道的其他因素。

应用程序是否应该知道它正在使用哪种语言环境/货币,并像这样传达价值

"amount": "1234.56"

请反复确认并信任该应用程序能够正确格式化金额?(此外:是否应避免使用小数值,而是以最小货币单位指定值?或者应将主要和次要单位列在不同的属性中?)服务器是否应提供原始值和格式化值?
"amount": "1234.56"
"displayAmount": "$1,234.56"

我应该提供原始值和货币代码,让应用程序进行格式化吗?例如:"amount": "1234.56","currencyCode": "USD"。我假设无论哪种方法都应该在传输到和从服务器传输时使用。
我无法找到标准 - 你有答案或可以指向定义此问题的资源吗?这似乎是一个常见的问题。

1
相关问题:https://dev59.com/tVcO5IYBdhLWcg3wy0n6 - Bennett Brown
5个回答

10

我不知道这是否是最好的解决方案,但我目前尝试的方法是只将值作为字符串传递,除小数点外不进行格式化,例如:

"amount": "1234.56"

该应用程序可以轻松解析它(并将其转换为double、BigDecimal、int或其他应用程序开发人员认为最适合浮点算术的方法)。 应用程序需要负责根据语言环境和货币格式化值以供显示。
该格式可以容纳其他货币值,无论是高度膨胀的大数字,还是小数点后有三位数字,或者根本没有小数部分的数字等。
当然,这假设应用程序已经知道所使用的语言环境和货币(来自另一个调用、应用程序设置或本地设备值)。 如果每次调用都需要指定这些值,则另一种选择是:
"amount": "1234.56",
"currency": "USD",
"locale": "en_US"

我有一个想法,将它们合并成一个JSON对象,但JSON源可能有多个用于不同目的的金额,并且只需要一次指定货币设置。当然,如果每个列出的金额可能会有所不同,那么最好将它们一起封装,如下所示:

{
"amount": "1234.56",
"currency": "USD",
"locale": "en_US"
}

另一个有争议的方法是服务器提供原始金额和格式化后的金额。(如果是这样,我建议将其封装为一个对象,而不是在提要中定义多个属性来定义相同的概念):

{
"displayAmount":"$1,234.56",
"calculationAmount":"1234.56"
}

在这里,更多的工作被转移到服务器。这也确保了在不同平台和应用程序中数字显示的一致性,同时为条件测试等提供了易于解析的值。
然而,它也留下了一个问题——如果应用程序需要执行计算,然后向用户显示结果怎么办?它仍然需要格式化数字以供显示。最好采用本回答顶部给出的第一个例子,并让应用程序控制格式化。
至少这是我的想法。我无法找到该领域的任何可靠的最佳实践或研究,因此我欢迎更好的解决方案或我未指出的潜在问题。

5
据我所知,JSON中没有“货币”标准——它是一种基于基本类型的标准。你需要考虑的事情是,有些货币没有小数部分(几内亚法郎、印度尼西亚盾),而有些货币可以被分成千分之一(巴林第纳尔)——因此你不应该假设两位小数。对于伊朗里亚尔来说,200万美元不会让你走得太远,所以我认为你需要处理双精度浮点数而不是整数。如果你正在寻找一个通用的国际模型,那么你将需要一个货币代码,因为通货膨胀率极高的国家经常每年或两年就更换货币,将价值除以1,000,000(或100亿)。历史上,巴西和伊朗都这样做过,我想。

如果你需要货币代码的参考(以及一些其他好的信息),那么请看这里:https://gist.github.com/Fluidbyte/2973986


1
谢谢您的想法。我已经指出了一些方法存在的问题。我正在寻找的是确实有效的方法。 - Chad Schultz

3

金额应该用字符串表示。

使用字符串的想法是,任何消费json的客户端都应该将其解析为十进制类型,例如BigDecimal,以避免浮点不精确性。

然而,只有在系统的任何部分都避免使用浮点时,这才有意义。即使后端只传递数据而不进行任何计算,使用浮点也最终会导致你看到的(程序中)与你得到的(json中)不同。

假设源是数据库,则重要的是将数据存储为正确的类型。如果数据已经存储为浮点数,则任何后续转换或强制转换都将是无意义的,因为它实际上是在传递不精确性。


1

可能没有任何官方标准。我们为我们的产品使用以下结构:

"amount": {
    "currency": "EUR",
    "scale": 2,
    "value": 875
}

上面的例子表示金额为€8.75。

货币被定义为字符串(其值应对应于ISO4217),比例和价值是整数。 "比例" 的含义是显而易见的。这种结构解决了许多货币没有小数,具有非标准小数等问题。


你在这里所谓的“精度”并不是指精度,而是指比例尺。对于那些知道精度和比例尺之间区别的人来说,这种约定将会非常令人困惑。 - Dawood ibn Kareem
@DawoodibnKareem:我同意,“scale”是应该用于这个属性的正确名称。谢谢。 - dexx

1

开发者门户网站 - API指南 - 货币上,您可以找到一些有趣的建议:

"price" : {
 "amount": 40,
 "currency": "EUR"
}

这比仅仅使用字符串更难生成和格式化,但我觉得这是最清晰和有意义的实现方式:

  1. 将金额和货币解耦
  2. 使用 number JSON 类型

这里建议使用的 JSON 格式为: https://pattern.yaas.io/v2/schema-monetary-amount.json

{
    "$schema": "http://json-schema.org/draft-04/schema#",
    "type": "object",
    "title": "Monetary Amount",
    "description":"Schema defining monetary amount in given currency.",
    "properties": {
        "amount": {
            "type": "number",
            "description": "The amount in the specified currency"
        },
        "currency": {
            "type": "string",
            "pattern": "^[a-zA-Z]{3}$",
            "description": "ISO 4217 currency code, e.g.: USD, EUR, CHF"
        }
    },
    "required": [
        "amount",
        "currency"
    ]
}

另一个与货币格式相关的问题指出,无论对错,这种做法更像是一个带有基本单位的字符串:

{
    "price": "40.0"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接