How Python types map to JSON

json · Essentials · 8 min

Silent changes (tuples to lists, int keys to strings) cause subtle bugs after a save and reload.

The encoder translates:

  • dict → object, list and tuple → array
  • str → string, int and float → number
  • True/False → true/false, None → null

Decoding goes the other way, with arrays always becoming list and numbers becoming int if they have no fraction or exponent, otherwise float.

What changes on a round trip:

  • Tuples come back as lists.
  • Dict keys become strings. JSON object keys must be strings, so {1: "a"} is written as {"1": "a"} and reads back with key "1". True and None keys become "true" and "null". Tuple keys raise TypeError (unless skipkeys=True, which silently drops them).
  • Mixed int and str keys that collide after conversion overwrite each other.

What isn't supported at all: set, bytes, datetime, Decimal, custom classes. Encoding them raises TypeError: Object of type set is not JSON serializable. The custom-encoding lessons show how to handle them.

float('nan') and infinities are written as NaN and Infinity, which are not valid JSON; strict parsers in other languages reject them.

Insertion order of dict keys is preserved both ways.

Example

import json

data = {"point": (3, 4), 1: "one", "ids": [1, 2]}
back = json.loads(json.dumps(data))
print(back)            # {'point': [3, 4], '1': 'one', 'ids': [1, 2]}
print(back == data)    # False

print(json.loads("[1, 1.0, 1e2]"))   # [1, 1.0, 100.0]
print(json.dumps({None: 0, True: 1}))  # {"null": 0, "true": 1}

for bad in [{1, 2}, b"raw", {(1, 2): "x"}]:
    try:
        json.dumps(bad)
    except TypeError as e:
        print(str(e)[:36])
# Object of type set is not JSON seria
# Object of type bytes is not JSON ser
# keys must be str, int, float, bool o

Remember

  • Tuples become lists and all dict keys become strings
  • Sets, bytes, datetime, Decimal and custom objects need explicit handling
  • NaN and Infinity are emitted but are not valid JSON

Watch out for

  • Saving a dict with int keys and then looking them up with ints after loading.

Check yourself

  1. cache = {200: 'OK'}; c2 = json.loads(json.dumps(cache)). What is c2.get(200)?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.python.org/3/library/json.html#py-to-json-table