Каков стандарт форматирования валютных значений в 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).
И, предполагая, что источником является база данных, важно, чтобы данные хранились с правильным типом. Если данные уже сохранены как числа с плавающей запятой, то любое последующее преобразование или преобразование будет бессмысленным, так как это технически приведет к неточности.