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:

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.

Table 1: Python integral numbers to JSON
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.

Table 2: Python floats to JSON
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
00001]
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
+49]
almost, outputs:
[5.18784831432e
+49]
no, outputs:
[51878483143195
924744806997083
865846970332907
307008.000000]
almost, outputs:
[5.18784831432e
+49]
2–8 [math.pi**-100] [1.927581416056
0206e-50]
yes almost, outputs:
[1.92758141606e
-50]
almost, outputs:
[1.92758141606e
-50]
no, outputs:
[0.000000]
almost, outputs:
[1.92758141606e
-50]

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.

Table 3: Atypical Python numbers to JSON
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.

Table 4: IEEE 754 non-numbers to JavaScript
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.

Table 5: JSON numbers to Python
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
567890123456789
012345679E+28")
]
yes, outputs:
[Decimal("12345
678901234567890
123456789.0")]
yes, outputs:
[1.234567890123
4568e+28]
yes, outputs:
[1.234567890123
4568e+28]
yes, outputs:
[1.234567890123
4568e+28]
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
000000000000000
000000000000000
00L]
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+
5000")]
no: error almost, outputs:
[«Infinity»]
no: error almost, outputs:
[«Infinity»]
5–14 [1.0e-5000] n/a yes, outputs:
[Decimal("1.0E-
5000")]
yes, outputs:
[Decimal("1.0E-
5000")]
almost, outputs:
[0.0]
no: error almost, outputs:
[0.0]
5–15 [-1.0e+5000] n/a yes, outputs:
[Decimal("-1.0E
+5000")]
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
5926535897931")
]
yes, outputs:
[Decimal("3.141
5926535897931")
]
yes yes yes
5–20 [3.141592653589
793238462643383
279502884197169
39937510]
[3.141592653589
7931]
yes, outputs:
[Decimal("3.141
592653589793238
462643383")]
yes, outputs:
[Decimal("3.141
592653589793238
462643383279502
884197169399375
10")]
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.

Table 6: Invalid JSON but legal JavaScript numbers to Python
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.

Table 7: Invalid JSON and invalid JavaScript numbers to Python
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

Go to the next page: Sequences