I-JSON [RFC7493] defines rules which enable interoperability between between different implementations. Although useful, many applications depend on extended numeric data which currently is dealt with in non-standard ways. This document describes some of the more common variants, with the goal of eventually establishing some kind of standard, be it de-facto or real.
The following JSON object highlights some of the issues with JSON numbers:
{
"giantNumber": 1.4e+9999,
"payMeThis": 26000.33,
"int64Max": 9223372036854775807
}
giantNumber
does not generally parse but could still be usable in certain contextspayMeThis
is probably not meant to be processed by a system based on traditional floating point due to potential rounding errorsint64Max
parses in all JSON systems but loses precision using ECMAScript'sJSON.parse()
Since I-JSON numbers are constrained by IEEE-754 double precision, extended numeric data types must in this specification be enclosed within double quotes, i.e. have JSON String syntax, permitting basic parsing and serialization of extended data types by virtually any JSON tool:
{
"giantNumber": "1.4e+9999"
}
A remaining issue is how a JSON parser can know how to recognize and process extended numeric data types. Fortunately, there are multiple solutions for that. Below is a programmatic variant (here expressed in ECMAScript). relying on name conventions between sender and receiver:
var obj = JSON.parse('{"giantNumber": "1.4e+9999"}');
var biggie = new BigNumber(obj.giantNumber);
In a declarative system, data types are usually resolved automatically like in this Java sample:
class MyObject {
String type;
BigInteger serialNumber;
}
Type | Description | Range | JSON Syntax * |
---|---|---|---|
Long | Signed 64-bit integer | -9223372036854775808 to 9223372036854775807 | 0|-?[1-9][0-9]* |
Ulong | Unsigned 64-bit integer | 0 to 18446744073709551615 | 0|[1-9][0-9]* |
BigInteger | Arbitrary large signed integer | -Arbitrary to Arbitrary | 0|-?[1-9][0-9]* |
BigDecimal | Arbitrary large decimal number | -Arbitrary to Arbitrary | -?[0-9]+(\.[0-9]+)?(e(-|\+)[0-9]+)? |
Money | Decimal number expressing money | -Sufficient to Sufficient | -?[1-9][0-9]*\.[0-9]+ |
* Minus the double quotes.
Sufficient denotes earthly monetary sums.
A few existing solutions have been investigated.
JSON-B currently uses Adaptive Notation where the actual value determines if it is to be serialized as a JSON Number or be embedded in a JSON String:
{
"BigDecimal.large": "1E+999",
"BigDecimal.small": 1,
"BigInteger.large": "240777489003222785532321",
"BigInteger.small": 1,
"Long.large": 9223372036854775807,
"Long.small": 1
}
This scheme is with the exception of "Long.large" compatible with JSON.parse()
;
Json.NET doesn't seem to have any global option governing number serialization. The defaults are:
{
"Decimal.large": 3777777447789999999445.678997,
"Decimal.small": 1.0,
"BigInteger.large": 240777489003222785532321,
"BigInteger.small": 1,
"Long.large": 9223372036854775807,
"Long.small": 1
}
This is not compliant with JSON.parse()
;
The NPM https://www.npmjs.com/package/bignumber.js relies on strings for BigNumber JSON serialization and parsing.
The W3C Payment Request API defines a monetary amount as
dictionary PaymentCurrencyAmount {
required DOMString currency;
required DOMString value;
};
where value
is a decimal number. The DOMString
declarator means that the value is
to be expressed as a JSON/ECMAScript String.
The following JSON extract from the Open Banking API shows that monetary amounts are expressed as decimal numbers enclosed in strings:
{
"amount": "259.99",
"currency": "GBP"
}
The Tweet Object shows the downside of not having a unified way of dealing with big numbers:
{
"created_at": "Thu Apr 06 15:24:15 +0000 2017",
"id": 850006245121695744,
"id_str": "850006245121695744",
"text": "This is what I'm saying :-)",
"user": {},
"entities": {}
}
V0.12