Каков стандарт форматирования валютных значений в JSON?

Принимая во внимание различные причуды типов данных и локализацию, каков наилучший способ для веб-службы передавать денежные значения в приложения и из них? Есть ли где-нибудь стандарт?

Моя первая мысль состояла в том, чтобы просто использовать тип номера. Например

"amount": 1234.56

Я видел много аргументов в отношении проблем с отсутствием точности и ошибок округления при использовании типов данных с плавающей точкой для денежных расчетов - однако мы просто передаем значение, а не вычисляем, так что это не имеет значения.

Спецификации валюты EventBrite JSON укажите что-то вроде этого:

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

Браво, чтобы избежать значений с плавающей запятой, но теперь мы сталкиваемся с другой проблемой: какое наибольшее число мы можем удерживать?

Один комментарий (я не знаю, действительно ли его правда, но кажется разумным) утверждает, что, поскольку модификации номеров различаются в JSON, лучшее, что вы можете ожидать, это 32- бит целое число со знаком. Наибольшее значение, которое может содержать 32-разрядное целое число со знаком, равно 2147483647. Если мы представляем значения в младшей единице, это составляет 21 474 836,47 долларов. $21 млн. Кажется огромным числом, но не исключено, что некоторые приложения могут нуждаться в работе со значением, большим, чем это. Проблема ухудшается с валютами, где 1000 второстепенной единицы составляют основную единицу или где валюта стоит меньше, чем доллар США. Например, Тунисский Динар разделен на 1000 миллионов. 2147483647 milim, или 2147483.647 TND составляет 1 124 492,04 доллара США. В некоторых случаях даже более вероятные значения более 1 миллиона долларов могут быть обработаны. Другой пример: подразделениями вьетнамских донов были бесполезны инфляция, поэтому давайте просто использовать крупные единицы. 2147483647 VND - $98 526,55. Я уверен, что многие случаи использования (банковские балансы, стоимость недвижимости и т.д.) Существенно выше. (EventBrite, вероятно, не стоит беспокоиться о том, что цены на билеты настолько высоки, хотя!)

Если мы избежим этой проблемы, передав значение как строку, как следует форматировать строку? Различные страны/регионы имеют совершенно разные форматы - разные символы валюты, независимо от того, существует ли символ до или после суммы, независимо от того, существует ли пробел между символом и суммой, если запятая или период используется для разделения десятичной, если запятые используются в качестве разделителя тысяч, круглых скобок или знака минус, чтобы указать отрицательные значения, и, возможно, более того, о которых я не знаю.

Если приложение знает, с какой локалью/валютой, с которой он работает, передайте значения, например

"amount": "1234.56"

назад и вперед, и доверьтесь приложению, чтобы правильно форматировать сумму? (Также: следует ли избегать десятичного значения и значение, указанное в терминах самой маленькой денежной единицы? Или следует указать основные и второстепенные единицы в разных свойствах?)

Или сервер должен предоставить исходное значение и отформатированное значение?

"amount": "1234.56"
"displayAmount": "$1,234.56"

Или должен ли сервер предоставить исходное значение и код валюты, и пусть приложение форматирует его?    "сумма": "1234,56"    "currencyCode": "USD" Я предполагаю, что какой бы метод использовался, он должен использоваться в обоих направлениях, передавая на сервер и с него.

Я не смог найти стандарт - есть ли у вас ответ или может указать мне ресурс, который определяет это? Это похоже на общую проблему.

Ответ 1

Я не знаю, является ли это лучшим решением, но теперь я пытаюсь просто передать значения как строки, не форматированные за исключением десятичной точки, например:

"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"
}

Здесь большая часть работы выгружается на сервер. Он также обеспечивает согласованность между различными платформами и приложениями в том, как отображаются числа, при этом обеспечивая легко анализируемое значение для условного тестирования и т.п.

Однако это оставляет проблему - что делать, если приложение должно выполнять вычисления, а затем показывать результаты пользователю? По-прежнему необходимо форматировать номер для отображения. Мог бы также пойти с первым примером в верхней части этого ответа и дать приложению контроль над форматированием.

Это мои мысли, по крайней мере. Я не смог найти каких-либо твердых передовых методов или исследований в этой области, поэтому я приветствую лучшие решения или потенциальные проблемы, о которых я не указал.

Ответ 2

AFAIK, в JSON нет стандарта "валюты" - это стандарт, основанный на рудиментарных типах. Вещи, которые вы, возможно, захотите рассмотреть, это то, что в некоторых валютах нет десятичной части (Guinean Franc, Indonesian Rupiah), а некоторые могут быть разделены на тысячные (Bahraini Dinar) - следовательно, вы не хотите брать два десятичных знака. Для иранских Real $2million не собирается вас далеко, так что я ожидаю, что вам придется иметь дело с двойниками не целыми числами. Если вы ищете общую международную модель, вам понадобится код валюты, так как страны с гиперинфляцией часто меняют валюты каждый год из двух, чтобы разделить значение на 1 000 000 (или 100 млн.). Я думаю, что исторически Бразилия и Иран сделали это.

Если вам нужна ссылка для кодов валют (и немного другой полезной информации), посмотрите здесь: https://gist.github.com/Fluidbyte/2973986

Ответ 3

ВКЛ Dev Portal - Руководство по API - Валюты вы можете найти интересные предложения:

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

Немного сложнее создать и форматировать, чем просто строку, но я считаю, что это самый чистый и осмысленный способ ее достижения:

  • количество и валюта разоблачения
  • использовать 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"
}

Ответ 4

Количество денег должно быть представлено в виде строки.

Идея использования строки состоит в том, что любой клиент, который использует json, должен анализировать его в десятичный тип, такой как BigDecimal чтобы избежать неточностей с плавающей запятой.

Однако это будет иметь смысл только в том случае, если какая-либо часть системы также избежит плавающей запятой. Даже если бэкэнд только передает данные и не выполняет никаких вычислений, использование плавающей запятой в конечном итоге приведет к тому, что вы видите (в программе) не то, что вы получаете (на json).

И, предполагая, что источником является база данных, важно, чтобы данные хранились с правильным типом. Если данные уже сохранены как числа с плавающей запятой, то любое последующее преобразование или преобразование будет бессмысленным, так как это технически приведет к неточности.