This report is out-of-date.
The state of things has changed dramatically, for the better, since I first wrote this in early 2008. Although my test cases are still quite useful, any information regarding specific python packages is likely to be inaccurate. I am leaving these pages here primarily for historic interest.
Numbers
Notation: Some of the examples on this page
refer to Python non-number types. Since the actual representation varies
among Python platforms, the following notations are used here to show these
python values:
«NaN», «Infinity», «−Infinity».
For details on the actual python object (which may differ between
platforms and between JSON modules), see the discussion below
about Python non-numbers.
Test environment
For these tests, a 64-bit Python 2.5 implementation is used, in which floats are stored internally as IEEE 754 double precision values. Some properties of this python are:
sys.maxint = 9223372036854775807(64-bit)math.pi = 3.1415926535897931floathas approximately 15 significant decimal digitsfloatsupports a maximum value of approximately 10307 (1.0E+307)- The standard Python
decimalmodule uses the default context of:prec = 28Emin = -999999999Emax = 999999999
About JSON numbers
It is important to realize that JSON only specifies the syntax for writing numerals in decimal notation, and does not deal with any semantics or meanings of actual numbers. JSON does not distinguish types, such as integer or floating point. Nor does JSON impose any size or precision limits on the numbers it can represent.
Where some meaning beyond the JSON specification is needed here, the JavaScript language interpretation is used. Furthermore, as JavaScript defers most of its floating-point treatment to the IEEE 754 standard, that standard will be referenced as well. Of particular interest is that +0.0 and -0.0 are required to be distinct non-equal number values (although JSON makes no statement one way or the other regarding equivalence). JSON has no syntax for representing IEEE non-numbers, such as NaN, even though JavaScript does.
About non-numbers in Python: NaN and Infinity
Some of the examples to follow make use of non-number numeric values. Neither Python or JSON explicitly support non-numbers; however JavaScript does. Additionally these special values may have other useful purposes for certain edge cases such as for conversion overflow and underflow; so we will deal with them here for completeness.
Generally there are three commonly used non-number values that have any usefulness: 1. (quiet) not-a-number (NaN), 2. positive infinity, and 3. negative infinity. The IEEE 754 standard actually defines other non-numbers too; but they have quite limited utility and we will ignore them.
As mentioned, the notations of «NaN», «Infinity», and «−Infinity» are being used on
this page for exposition as a placeholder for some specific Python object
representing a non-number value. The actual underlying Python object
can vary between Python platforms and implementations, as well as
between the JSON module being used.
Python implementations built on an IEEE 754-compliant
floating-point library (which is probably most of them) can represent
such non-numbers with the builtin float type.
Technically this is not part of the Python language, but rather a
side-effect of the implementation. On many platforms these values can
be constructed with float('nan'),
float('inf'), and float('-inf'). But this
is not portable. For example on the AIX/PowerPC platform they are
constructed using float('NaNQ'),
float('INF'), and float('-INF') instead. On
yet other platforms the method to generate such non-number floats may
involve explict bit manipulation tricks, usually with the
struct module, taking into account the endianness of the
platform.
Also in Python 2.4 or later, the standard decimal
module also has it's own independent implementation of
non-numbers. These are known as decimal.Nan,
decimal.Inf, and decimal.negInf; or
alternatively by using the constructors Decimal('NaN'),
Decimal('Infinity'), and
Decimal('-Infinity').
The demjson and simplejson modules explicitly try to
support IEEE 754 non-number float objects, when
available. Furthermore, demjson will alternatively use the
decimal module if available; and will even provide its
own emulation types if no other suitable non-numbers types are
found.
The jsonlib module, despite using the decimal
module heavily, explicitly prohibits use any of it's non-number values
(which are never needed for strict JSON conformance).
Converting Python numbers to JSON
Integral types
Python integral types (int and long)
should be convertable directly to JSON. Since JSON imposes no size
limits all Python integers can be represented by JSON; however,
remember that many other JSON implementations in other languages may
not be able to handle these large numbers.
| Test# | from Python | to JSON | demjson | jsonlib | python-cjson | python-json | simplejson |
|---|---|---|---|---|---|---|---|
| 1–1 | [0] | [0] | yes | yes | yes | yes | yes |
| 1–2 | [1] | [1] | yes | yes | yes | yes | yes |
| 1–3 | [123456789] | [123456789] | yes | yes | yes | yes | yes |
| 1–4 | [-123456789] | [-123456789] | yes | yes | yes | yes | yes |
| 1–5 | [sys.maxint] | [92233720368547 75807] |
yes | yes | yes | yes | yes |
| 1–6 | [10**22+57] | [10000000000000 000000057] |
yes | yes | yes | yes | yes |
| 1–7 | [-10**22+57] | [-9999999999999 999999943] |
yes | yes | yes | yes | yes |
Floating point
Python floating point numbers (float) should be
representable as JSON. JSON represents numbers with decimal
fractions, and optional base-10 exponents.
A floating-point number with a zero a fractional part, such
as 1.0, could reasonably be converted to the JSON
number 1 as well as 1.0; however
no implementations choose to drop the fractional part.
Concerning precision: In some cases a loss of precison may
occur (least-significant digits are dropped). No concern is given
here for how the last two fractional digits are handled as they are
likely to be inexact anyway; so only the loss of three or more digits
is considered important. The loss of digits is probably caused by
implementations that use either str(f) or
"%f"%f, rather than repr(f). Since JSON is
primarily intended as a data interchange format, a loss of precision
is probably not as acceptable as it would be if intended for human
consumption.
| Test# | from Python | to JSON | demjson | jsonlib | python-cjson | python-json | simplejson |
|---|---|---|---|---|---|---|---|
| 2–1 | [0.0] | [0.0] | yes | yes | yes | yes, outputs:[0.000000] |
yes |
| 2–2 | [-0.0] | [-0.0] | yes | yes, outputs:[0.0] |
yes | yes, outputs:[-0.000000] |
yes |
| 2–3 | [1.0] | [1.0] | yes | yes | yes | yes, outputs:[1.000000] |
yes |
| 2–4 | [0.1] | [0.1] | yes, outputs:[0.100000000000 |
yes | yes | yes, outputs:[0.100000] |
yes |
| 2–5 | [0.5] | [0.5] | yes | yes | yes | yes, outputs:[0.500000] |
yes |
| 2–6 | [math.pi] | [3.141592653589 7931] |
yes | almost, outputs:[3.14159265359] |
almost, outputs:[3.14159265359] |
no, outputs:[3.141593] |
almost, outputs:[3.14159265359] |
| 2–7 | [math.pi**100] | [5.187848314319 5925e+49] |
yes | almost, outputs:[5.18784831432e |
almost, outputs:[5.18784831432e |
no, outputs:[51878483143195 |
almost, outputs:[5.18784831432e |
| 2–8 | [math.pi**-100] | [1.927581416056 0206e-50] |
yes | almost, outputs:[1.92758141606e |
almost, outputs:[1.92758141606e |
no, outputs:[0.000000] |
almost, outputs:[1.92758141606e |
Other number types: complex and decimal
Python supports additional number types other than the integer and
floating types
(int,long,float). Since JSON
can only represent decimal numbers, it is not expected that these
other Python numeric types can be converted to Python. There may
however be a few cases where, mathematically speaking, such a conversion
may be possible.
| Test# | from Python | to JSON | demjson | jsonlib | python-cjson | python-json | simplejson |
|---|---|---|---|---|---|---|---|
| 3–1 | [(5+0j)] | [5] | yes | no: error | no: error | no: error | no: error |
| 3–2 | [(5.5+0j)] | [5.5] | yes | no: error | no: error | no: error | no: error |
| 3–3 | [(5+4j)] | n/a | yes: error | yes: error | yes: error | yes: error | yes: error |
| 3–4 | [Decimal(5)] | [5] | yes | yes | no: error | no: error | no: error |
| 3–5 | [Decimal("0.1") ] |
[0.1] | yes | yes | no: error | no: error | no: error |
| 3–6 | [Decimal("1.234 5e+91023")] |
[1.2345E+91023] | yes | yes, but SLOW | no: error | no: error | no: error |
| 3–7 | [Decimal("3.141 592653589793238 462643383279502 884197169399375 10")] |
[3.141592653589 793238462643383 279502884197169 39937510] |
yes | yes | no: error | no: error | no: error |
Non-numbers
The Python decimal type, introduced in Python 2.4, also supports non-numbers. It does so in a platform neutral way that does not depend upon any underlying IEEE 754 foundation.
Note: supporting non-numbers is not required for JSON conformance.
| Test# | from Python | to JavaScript | demjson | jsonlib | python-cjson | python-json | simplejson |
|---|---|---|---|---|---|---|---|
| 4–1 | [«NaN»] | [NaN] | yes | no: error | yes | no, outputs:[nan] |
yes |
| 4–2 | [«Infinity»] | [Infinity] | yes | no: error | yes | no, outputs:[inf] |
yes |
| 4–3 | [«−Infinity»] | [-Infinity] | yes | no: error | yes | no, outputs:[-inf] |
yes |
| 4–4 | [Decimal("NaN") ] |
[NaN] | yes | no: error | no: error | no: error | no: error |
| 4–5 | [Decimal("Infin ity")] |
[Infinity] | yes | no: error | no: error | no: error | no: error |
| 4–6 | [Decimal("-Infi nity")] |
[-Infinity] | yes | no: error | no: error | no: error | no: error |
Converting JSON numbers to Python
There are not different types of JSON numbers, so when converting
them to Python a choice must be made as to which Python numeric type
will be created. Generally it makes sense for whole numbers to be
converted into Python int or long types,
whereas those with fractional parts into a Python
float.
There are some non-obvious cases though, for example the JSON
number 5e+20 could be converted to either
float(5.0e+20) or the integer 500000000000000000000L.
Either would be correct.
Also concerning negative zero, -0. The JSON
specification allows that number, but does not define what it means.
However, the JavaScript specification (actually IEEE 754) says that -0
and +0 must be kept as distinct values. Since Python does not have an
integer -0 though, it could be acceptable for the JSON -0
to be converted to either the Python integer 0 or float
-0.0.
| Test# | from JSON | to Python | demjson | jsonlib | python-cjson | python-json | simplejson |
|---|---|---|---|---|---|---|---|
| 5–1 | [0] | [0] | yes | yes, outputs:[0L] |
yes | yes | yes |
| 5–2 | [-0] | [0] | yes, outputs:[-0.0] |
yes, outputs:[0L] |
yes | yes | yes |
| 5–3 | [-0.0] | [-0.0] | yes | yes, outputs:[Decimal("-0.0" |
yes | yes | yes |
| 5–4 | [42] | [42] | yes | yes, outputs:[42L] |
yes | yes | yes |
| 5–5 | [-42] | [-42] | yes | yes, outputs:[-42L] |
yes | yes | yes |
| 5–6 | [12345678901234 567890123456789 ] |
[12345678901234 567890123456789 L] |
yes | yes | yes | yes | yes |
| 5–7 | [12345678901234 567890123456789 .0] |
[12345678901234 567890123456789 L] |
yes, outputs:[Decimal("1.234 |
yes, outputs:[Decimal("12345 |
yes, outputs:[1.234567890123 |
yes, outputs:[1.234567890123 |
yes, outputs:[1.234567890123 |
| 5–8 | [5E3] | [5000] | yes, outputs:[5000] |
yes, outputs:[Decimal("5E+3" |
yes, outputs:[5000.0] |
no | yes, outputs:[5000.0] |
| 5–9 | [5e+3] | [5000] | yes, outputs:[5000] |
no | yes, outputs:[5000.0] |
no | yes, outputs:[5000.0] |
| 5–10 | [500e-3] | [0.5] | yes | yes, outputs:[Decimal("0.500 |
yes | no | yes |
| 5–11 | [5e+45] | [5e+45] | yes, outputs:[50000000000000 |
no | yes | no | yes |
| 5–12 | [1.0e+500000000 000] |
n/a | almost, outputs:[«Infinity»] |
no: error | almost, outputs:[«Infinity»] |
no: error | almost, outputs:[«Infinity»] |
| 5–13 | [1.0e+5000] | n/a | yes, outputs:[Decimal("1.0E+ |
no: error | almost, outputs:[«Infinity»] |
no: error | almost, outputs:[«Infinity»] |
| 5–14 | [1.0e-5000] | n/a | yes, outputs:[Decimal("1.0E- |
yes, outputs:[Decimal("1.0E- |
almost, outputs:[0.0] |
no: error | almost, outputs:[0.0] |
| 5–15 | [-1.0e+5000] | n/a | yes, outputs:[Decimal("-1.0E |
no: error | almost, outputs:[«−Infinity»] |
no: error | almost, outputs:[«−Infinity»] |
| 5–16 | [-1.0e+50000000 0000] |
n/a | almost, outputs:[«−Infinity»] |
no: error | almost, outputs:[«−Infinity»] |
no: error | almost, outputs:[«−Infinity»] |
| 5–17 | [1.234e+3] | [1234] | yes, outputs:[1234.0] |
no | yes, outputs:[1234.0] |
no | yes, outputs:[1234.0] |
| 5–18 | [0.1] | [0.100000000000 00001] |
yes | yes, outputs:[Decimal("0.1") |
yes | yes | yes |
| 5–19 | [3.141592653589 7931] |
[3.141592653589 7931] |
yes, outputs:[Decimal("3.141 |
yes, outputs:[Decimal("3.141 |
yes | yes | yes |
| 5–20 | [3.141592653589 793238462643383 279502884197169 39937510] |
[3.141592653589 7931] |
yes, outputs:[Decimal("3.141 |
yes, outputs:[Decimal("3.141 |
yes | yes | yes |
Invalid JSON numbers
The JSON syntax is much stricter than that of JavaScript for numbers. Thus there are numbers which may be valid JavaScript that are not valid JSON. A JSON parser may wish to accept those numbers; however, it should always result in the same numeric value as what JavaScript defines. In particular, JavaScript supports hexadecimal and octal numbers (although octal is only in older JavaScript versions), so an octal number must not be interpreted as decimal.
Non-numbers: JavaScript also supports IEEE non-numbers, in
particular NaN (not a number), Infinity, and
-Infinity. Python does not directly support non-numbers;
although certain implementations of Python may indirectly do so via
the underlying platform floating point library. Implementations which
are based on an IEEE 754 floating point library should be able to
represent such non-numbers. However, if the Python implementation is
not built on IEEE 754 floats, only the demjson still attempts
to parse JavaScript non-numbers by returning demjson.nan,
demjson.inf, or demjson.neginf simulated
numeric values.
| Test# | Invalid JSON Legal JavaScript |
demjson/strict | demjson/loose | jsonlib | python-cjson | python-json | simplejson |
|---|---|---|---|---|---|---|---|
| 6–1 | [+0] | yes: error | almost, outputs:[0] |
yes: error | almost, outputs:[0] |
yes: error | yes: error |
| 6–2 | [--5] | yes: error | almost, outputs:[5] |
yes: error | yes: error | yes: error | yes: error |
| 6–3 | [---5] | yes: error | almost, outputs:[-5] |
yes: error | yes: error | yes: error | yes: error |
| 6–4 | [.0] | yes: error | almost, outputs:[0.0] |
yes: error | yes: error | yes: error | yes: error |
| 6–5 | [034] | yes: error | almost, outputs:[28] |
yes: error | no, outputs:[34] |
no, outputs:[34] |
yes: error |
| 6–6 | [080] | yes: error | yes: error | yes: error | no, outputs:[80] |
no, outputs:[80] |
yes: error |
| 6–7 | [0x80] | yes: error | almost, outputs:[128] |
yes: error | yes: error | yes: error | yes: error |
| 6–8 | [0xfB7] | yes: error | almost, outputs:[4023] |
yes: error | yes: error | yes: error | yes: error |
| 6–9 | [NaN] | yes: error | almost, outputs:[«NaN»] |
yes: error | almost, outputs:[«NaN»] |
yes: error | almost, outputs:[«NaN»] |
| 6–10 | [Infinity] | yes: error | almost, outputs:[«Infinity»] |
yes: error | almost, outputs:[«Infinity»] |
yes: error | almost, outputs:[«Infinity»] |
| 6–11 | [-Infinity] | yes: error | almost, outputs:[«−Infinity»] |
yes: error | almost, outputs:[«−Infinity»] |
yes: error | almost, outputs:[«−Infinity»] |
If a number is neither valid JSON nor valid JavaScript, then the JSON parser should reject it with an error. However since the Python syntax for numbers is even more liberal than that of JavaScript, some parsers may incorrectly accept such invalid numbers.
| Test# | Invalid JSON Invalid JavaScript |
demjson/strict | demjson/loose | jsonlib | python-cjson | python-json | simplejson |
|---|---|---|---|---|---|---|---|
| 7–1 | [0.] | yes: error | yes: error | almost, outputs:[Decimal("0")] |
almost, outputs:[0.0] |
almost, outputs:[0.0] |
yes: error |

