Python's nonstandard JSON encoding
This was last updated for Python 3.11.4.
Python’s built-in JSON module, json
, can produce results that most other JSON parsers do not accept. This is because it has nonstandard serializations for infinity, negative infinity, and NaN.
import json
import math
json.dumps([math.inf, -math.inf, math.nan])
# => "[Infinity, -Infinity, NaN]"
Most other languages and libraries can’t decode this. For example, JSON.parse("Infinity")
throws an error in JavaScript.
To avoid this, you can pass the allow_nan
option, which will throw an error if you try to serialize these values. (Though it’s called “allow NaN”, it also throws if passed inf
.)
json.dumps(math.nan, allow_nan=False)
# ValueError: Out of range float values are not JSON compliant
json.dumps(math.inf, allow_nan=False)
# ValueError: Out of range float values are not JSON compliant
I tested the following languages and libraries, all of which fail to parse these values:
- C++: nlohmann::json
- Clojure: data.json
- Crystal:
JSON.parse()
- Dart:
json.decode()
- Go: encoding/json
- Java: Jackson
- Java: org.json parses unquoted strings so these values are parsed as the string “Infinity” or “NaN”, but this is not a special case. For example, this is no different from parsing the string “foo”.
- JavaScript:
JSON.parse()
- PHP:
json_decode()
- Ruby:
JSON.parse()
fails by default, though theallow_nan
option will parse these values - Rust: Serde JSON
- Swift:
JSONSerialization.jsonObject(with:)
However, there are a few places that parse these just fine:
- Python, of course
- Java’s Gson
- jq, which also parses
inf
and-inf
- SQLite’s JSON functions, though its
json_valid()
function returns0
for these strings
These values are also valid JSON5. JSON5 “is not intended to be used for machine-to-machine communication”, but can parse the values “Infinity”, “-Infinity”, and “NaN”.
I found Python’s behavior pretty surprising, so I thought I’d write it up!