web-dev-qa-db-fra.com

Quelle est la norme pour formater les valeurs monétaires en JSON?

Compte tenu des particularités des types de données et de la localisation, quel est le meilleur moyen pour un service Web de communiquer des valeurs monétaires à destination et en provenance d'applications? Y a-t-il une norme quelque part?

Ma première pensée a été d'utiliser simplement le type de numéro. Par exemple

"amount": 1234.56

J'ai vu beaucoup d'arguments concernant des problèmes avec un manque de précision et des erreurs d'arrondi lors de l'utilisation de types de données à virgule flottante pour les calculs monétaires. Cependant, nous transmettons simplement la valeur, et non pas le calcul, cela n'a donc aucune importance.

Spécifications de la devise JSON d'EventBrite spécifiez quelque chose comme ceci:

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

Bravo pour avoir évité les valeurs en virgule flottante, mais nous nous heurtons maintenant à un autre problème: quel est le plus grand nombre que nous pouvons détenir?

Un commentaire ((Je ne sais pas si c'est vrai, mais cela semble raisonnable) affirme que, puisque les implémentations de nombres varient en JSON, le meilleur moyen de s'attendre est un entier signé de 32 bits. La plus grande valeur qu'un entier signé 32 bits puisse contenir est 2147483647. Si nous représentons des valeurs dans l'unité secondaire, cela représente 21 474 836,47 $. 21 millions de dollars, cela semble être un chiffre énorme, mais il n’est pas inconcevable que certaines applications aient besoin de travailler avec une valeur supérieure à celle. Le problème s'aggrave avec les monnaies où 1 000 des unités mineures forment une unité majeure ou dont la valeur vaut moins que le dollar américain. Par exemple, un dinar tunisien est divisé en 1 000 milim. 2147483647 milim, ou 2147483.647 TND est de 1 124 492,04 $. Il est encore plus probable que des valeurs supérieures à un million de dollars puissent être utilisées dans certains cas. Autre exemple: les sous-unités du dong vietnamien ont été rendues inutilisables par l’inflation, alors utilisons simplement les principales unités. 2147483647 VND est 98 526,55 $. Je suis sûr que de nombreux cas d'utilisation (soldes bancaires, valeurs immobilières, etc.) sont considérablement plus élevés que cela. (EventBrite n’a probablement pas à s’inquiéter du prix aussi élevé des billets!)

Si nous évitons ce problème en communiquant la valeur sous forme de chaîne, comment la chaîne doit-elle être formatée? Différents pays/régions ont des formats très différents: symboles de devise différents, que le symbole apparaisse avant ou après le montant, qu’il y ait ou non un espace entre le symbole et le montant, si une virgule ou une période est utilisée pour séparer la décimale. sont utilisés comme séparateur de milliers, parenthèses ou signe moins pour indiquer des valeurs négatives, et peut-être davantage que je ne connais pas.

Si l’application sait avec quels paramètres régionaux/devise elle travaille, communiquez des valeurs telles que

"amount": "1234.56"

en arrière, et faire confiance à l'application pour formater correctement le montant? (En outre: faut-il éviter la valeur décimale et la valeur spécifiée en fonction de la plus petite unité monétaire? Ou les unités majeure et secondaire devraient-elles figurer dans des propriétés différentes?)

Ou le serveur doit-il fournir la valeur brute et la valeur formatée?

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

Ou le serveur doit-il fournir la valeur brute et le code de devise et laisser l'application le formater? "montant": "1234.56" "currencyCode": "USD" Je suppose que la méthode utilisée doit être utilisée dans les deux sens, en transmettant vers et depuis le serveur.

J'ai été incapable de trouver la norme - avez-vous une réponse ou pouvez-vous m'indiquer une ressource qui la définit? Cela semble être un problème commun.

22
Chad Schultz

Je ne sais pas si c'est la meilleure solution, mais ce que j'essaie maintenant, c'est simplement de transmettre des valeurs sous forme de chaînes non formatées, à l'exception d'un point décimal, comme suit:

"amount": "1234.56"

L'application pourrait facilement analyser cela (et la convertir en double, BigDecimal, int ou quelle que soit la méthode que le développeur de l'application considère le mieux pour l'arithmétique en virgule flottante). L'application serait chargée de formater la valeur pour l'affichage en fonction des paramètres régionaux et de la devise. 

Ce format peut prendre en charge d’autres valeurs monétaires, qu’il s’agisse de grands nombres très gonflés, de trois chiffres après le point décimal, de nombres sans aucune fraction, etc.

Bien entendu, cela supposerait que l'application connaisse déjà les paramètres régionaux et la devise utilisée (provenant d'un autre appel, d'un paramètre d'application ou des valeurs de périphérique local). Si ceux-ci doivent être spécifiés par appel, une autre option serait:

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

Je suis tenté de les combiner dans un seul objet JSON, mais un flux JSON peut avoir plusieurs montants à des fins différentes, et il suffirait ensuite de spécifier les paramètres de devise une fois. Bien sûr, si cela pouvait varier pour chaque montant indiqué, il serait alors préférable de les encapsuler ensemble, comme suit:

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

Une autre approche discutable est que le serveur fournisse la quantité brute et la quantité formatée. (Si tel est le cas, je suggérerais de l'encapsuler en tant qu'objet, au lieu d'avoir plusieurs propriétés dans un flux qui définissent toutes le même concept):

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

Ici, une plus grande partie du travail est déchargée sur le serveur. Il garantit également la cohérence de l'affichage des chiffres sur différentes plates-formes et applications, tout en fournissant une valeur facilement analysable pour les tests conditionnels et autres.

Cependant, cela laisse un problème - que se passe-t-il si l'application doit effectuer des calculs puis afficher les résultats à l'utilisateur? Il faudra quand même formater le numéro pour l'affichage. Pourriez aussi bien aller avec le premier exemple en haut de cette réponse et donner à l'application le contrôle de la mise en forme.

Ce sont mes pensées, au moins. Je suis incapable de trouver de bonnes pratiques ou des recherches solides dans ce domaine. Je suis donc ouvert à de meilleures solutions ou à des pièges potentiels que je n'ai pas signalés.

5
Chad Schultz

D’après nos connaissances, il n’existe pas de norme "devise" en JSON - c’est une norme basée sur des types rudimentaires. Vous voudrez peut-être prendre en compte le fait que certaines monnaies n'ont pas de partie décimale (franc guinéen, roupie indonésienne) et que certaines peuvent être divisées en millièmes (dinar bahreïnien). Par conséquent, vous ne voulez pas prendre deux décimales. Pour le réel iranien, 2 millions de dollars ne vous mèneront pas loin, alors je suppose que vous devez traiter avec des doubles et non des entiers. Si vous recherchez un modèle international général, vous aurez besoin d'un code de devise, car les pays avec une hyperinflation changent souvent de devise chaque année sur deux pour diviser la valeur par 1 000 000 (ou 100 millions). Historiquement, le Brésil et l’Iran ont fait cela, je pense.

Si vous avez besoin d’une référence pour les codes de devise (et un peu d’autres bonnes informations), consultez la page: https://Gist.github.com/Fluidbyte/2973986

3
Paul Coldrey

ON Dev Portal - Directives de l'API - Devises vous pouvez trouver des suggestions intéressantes:

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

Il est un peu plus difficile de produire et de formater qu'une simple chaîne, mais j'estime qu'il s'agit du moyen le plus propre et le plus significatif de le réaliser:

  1. découpler montant et devise 
  2. utilisez numberJSON type 

Voici le format JSON suggéré: 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"
    ]
}

Un autre questions liées au format de la devise a souligné, à tort ou à raison, que la pratique ressemblait beaucoup plus à une chaîne avec des unités de base:

{
    "price": "40.0"
}
1
Jean-Rémy Revy

La quantité d'argent doit être représentée sous forme de chaîne. 

L'idée d'utiliser string est que tout client utilisant le JSON doit l'analyser en type décimal, tel que BigDecimal, pour éviter toute imprécision en virgule flottante. 

Cependant, cela n'aurait de sens que si une partie du système évitait aussi la virgule flottante. Même si le système ne fait que transmettre des données et ne faire aucun calcul, l'utilisation de la virgule flottante aura pour résultat que ce que vous voyez (dans le programme) n'est pas ce que vous obtenez (sur le json). 

Et en supposant que la source soit une base de données, il est important que les données soient stockées avec le bon type. Si les données sont déjà stockées sous forme de virgule flottante, les conversions ou transformations ultérieures n'auraient aucune signification, car elles contiendraient techniquement des imprécisions. 

1
inmyth