2015-05-14 38 views
14

Pamiętając o różnych dziwactwach typów danych i lokalizacji, jaki jest najlepszy sposób, aby usługa sieciowa komunikowała wartości pieniężne do iz aplikacji? Czy gdzieś istnieje jakiś standard?Jaki jest standard formatowania wartości walut w JSON?

Moją pierwszą myślą było po prostu użyć typu numeru. Na przykład

"amount": 1234.56 

Widziałem wiele argumentów o problemach z brakiem precyzji i błędów zaokrąglania podczas korzystania pływających typów danych punktowych dla obliczeń pieniężnej - jednak jesteśmy tylko przekazywaniu wartości, a nie obliczenia, tak że powinnam Nieważne.

EventBrite's JSON currency specifications określić coś takiego:

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

Bravo dla uniknięcia pływających wartości punktowe, ale teraz możemy uruchomić w innej kwestii: co to największa liczba możemy trzymać?

One comment (Nie wiem, czy to prawda, ale wydaje się uzasadnione) twierdzi, że ponieważ implementacje liczb różnią się w JSON, najlepsze, czego można się spodziewać, to 32-bitowa liczba całkowita ze znakiem. Największa wartość, jaką może pomieścić 32-bitowa liczba całkowita ze znakiem, wynosi 2147483647. Jeśli reprezentujemy wartości w jednostce podrzędnej, jest to 21 474 846,47 USD. 21 milionów dolarów wydaje się być ogromną liczbą, ale nie jest wykluczone, że niektóre aplikacje mogą wymagać większej wartości. Problem pogłębia się w przypadku walut, w których 1000 jednostki mniejszej tworzy główną jednostkę, lub gdy waluta jest warta mniej niż dolar amerykański. Na przykład dinar tunezyjski jest podzielony na 1000 mil. 2147483647 milim, czyli 2147483.647 TND wynosi 1 124 492,04 USD. Jest jeszcze bardziej prawdopodobne, że w niektórych przypadkach można pracować z wartościami powyżej 1 miliona USD. Inny przykład: podjednostki wietnamskiego dongu stały się bezużyteczne z powodu inflacji, więc używajmy głównych jednostek. 2147483647 VND to 98.522,55 £. Jestem pewien, że wiele przypadków użycia (salda bankowe, wartości nieruchomości itp.) Jest znacznie wyższe. (EventBrite prawdopodobnie nie musi się martwić, że ceny biletów są tak wysokie!)

Jeśli unikniemy tego problemu, przekazując wartość jako łańcuch, w jaki sposób powinien być sformatowany ciąg znaków? Różne kraje/lokalizacje mają drastycznie różne formaty - różne symbole walut, niezależnie od tego, czy symbol występuje przed czy po kwocie, niezależnie od tego, czy między symbolem a kwotą jest spacja, czy przecinek lub kropka są używane do rozdzielenia części dziesiętnej, czy przecinki są używane jako separatory tysięcy, nawiasy lub znak minus, aby wskazać wartości ujemne i być może więcej, o których nie wiem.

Jeżeli aplikacja wie, co locale/waluta to działa z, komunikować wartości jak

"amount": "1234.56" 

tam iz powrotem, i uwierzcie aplikację do poprawnego formatowania kwotę? (Także: czy należy unikać wartości dziesiętnej i wartości określonej w najmniejszej jednostce pieniężnej? Czy jednostka główna i drugorzędna powinna być wymieniona w różnych nieruchomościach?)

Czy serwer powinien podać wartość początkową i sformatowana wartość?

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

Czy serwer powinien podać wartość początkową i kod waluty, a następnie pozwolić aplikacji na sformatowanie? "amount": "1234.56" "currencyCode": "USD" Zakładam, że dowolna z metod jest używana w obu kierunkach, przesyłana do iz serwera.

Nie mogę znaleźć standardu - czy masz odpowiedź, czy możesz wskazać mi zasób, który to definiuje? Wydaje się, że jest to typowy problem.

+1

pokrewne pytanie: https: //stackoverflow.com/questions/45222706/what-are-the-best-practices-passing-dollar-amounts-in-json –

Odpowiedz

2

AFAIK, nie ma standardu "waluty" w JSON - jest to standard oparty na elementarnych typach. Rzeczy, które chcesz wziąć pod uwagę, to fakt, że niektóre waluty nie mają części dziesiętnej (frank gwinejski, indonezyjska rupia), a niektóre mogą być podzielone na tysięczne (dinar bahrajski) - dlatego nie chcesz przyjmować dwóch miejsc po przecinku. Dla irańskich Real 2 miliony dolarów nie doprowadzi cię daleko, więc oczekiwałbym, że będziesz musiał radzić sobie z podwójnymi, a nie liczbami całkowitymi. Jeśli szukasz ogólnego modelu międzynarodowego, będziesz potrzebować kodu waluty, ponieważ kraje z hiperinflacją często zmieniają waluty co roku o dwóch, aby podzielić wartość na 1 000 000 (lub 100 milionów). Historycznie rzecz biorąc, Brazylia i Iran zrobiły to, jak sądzę.

Jeśli potrzebujesz odniesienie do kodów walutowych (i trochę innych dobrych informacji) a następnie spójrz tutaj: https://gist.github.com/Fluidbyte/2973986

+0

Dziękuję za twoje myśli. Wskazałem już na kilka problemów z niektórymi podejściami. To, czego szukam, to coś, co działa. –

4

Nie wiem, czy jest to najlepsze rozwiązanie, ale co usiłuję teraz jest po prostu przekazać wartości jako ciągi niesformatowany wyjątkiem punktu dziesiętnego, tak:

"amount": "1234.56" 

Aplikacja może łatwo analizować, że (i przekształcić ją podwoić, BigDecimal, int, lub cokolwiek metoda aplikacji deweloper czuje się najlepiej dla Arytmetyka zmiennoprzecinkowa). Aplikacja będzie odpowiedzialna za formatowanie wartości wyświetlania zgodnie z ustawieniami narodowymi i walutą.

Format ten mógł pomieścić inne wartości walut, czy bardzo zawyżonych wielkich liczb, cyfr z trzema cyframi po przecinku, bez żadnych wartości liczb ułamkowych wcale, itp

Oczywiście, to przyjęłoby już aplikację zna używane ustawienia narodowe i walutę (z innego połączenia, ustawienia aplikacji lub lokalnych wartości urządzeń). Jeśli te muszą być określone za rozmowę, innym rozwiązaniem byłoby:

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

Kusi mnie, aby rzucić je w jeden obiekt JSON, ale pasza JSON może mieć wiele kwot do różnych celów, a wtedy wystarczy raz określić ustawienia waluty. Oczywiście, jeśli to może się różnić dla każdej kwoty wymienione, to najlepiej byłoby ująć je razem, tak jak poniżej:

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

Innym podejściem jest dyskusyjna na serwer w celu zapewnienia surowego ilość i sformatowaną kwotę. (Jeśli tak, chciałbym zaproponować enkapsulacji go jako obiekt, zamiast wielu właściwości w paszach, że wszystkie zdefiniowania samego pojęcia):

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

Tutaj więcej pracy są wyładowywane do serwera. Zapewnia również spójność między różnymi platformami i aplikacjami w zakresie wyświetlania liczb, a jednocześnie zapewnia łatwą do przeanalizowania wartość dla testów warunkowych i tym podobnych.

Pozostaje jednak problem - co zrobić, jeśli aplikacja musi wykonać obliczenia, a następnie wyświetlić wyniki użytkownikowi? Nadal będzie trzeba sformatować numer do wyświetlenia. Równie dobrze można pójść z pierwszym przykładem u góry tej odpowiedzi i dać aplikacji kontrolę nad formatowaniem.

To są przynajmniej moje myśli. Nie mogłem znaleźć żadnych dobrych dobrych praktyk lub badań w tej dziedzinie, więc z zadowoleniem przyjmuję lepsze rozwiązania lub potencjalne pułapki, o których nie wspomniałem.

0

NA Dev Portal - API Guidelines - Currencies można znaleźć ciekawe propozycje:

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

Jest to nieco trudniejsze do wytworzenia formatu & niż tylko ciąg, ale czuję, że to jest najczystszym i wymowny sposób, żeby to osiągnąć:

  1. Odłączyć kwota i waluta
  2. użycie numberJSON typ

Tutaj format JSON sugeruje: 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" 
    ] 
} 

Innym questions related to currency format wskazał prawo lub błędnie, że praktyka jest o wiele bardziej jak struna z jednostkami bazowymi:

{ 
    "price": "40.0" 
}