🎓 A1Learner
🔍

Python JSON in Depth

A round trip saves data and loads it back again. Some values change in silence on that trip.

Keys change in silence

This dict has number keys. Watch what the keys become after the round trip.

import json

data = {1: "one", 2: "two"}
text = json.dumps(data, sort_keys=True)
print(text)
loaded = json.loads(text)
print(list(loaded.keys()))

JSON spelling

JSON has its own spelling for three Python values. The words are all small letters.

import json

print(json.dumps({"ready": True, "done": False, "extra": None}))

Help dumps with default

dumps cannot save a set by itself. A default function gets called for such values, and sorted turns the set into a list.

import json

print(json.dumps({"nums": {3, 1, 2}}, default=sorted))

Build objects with object_hook

object_hook runs on each dict that loads makes. This one turns a point dict into a tuple.

import json

def to_point(data):
    if "x" in data and "y" in data:
        return (data["x"], data["y"])
    return data

point = json.loads('{"x": 2, "y": 3}', object_hook=to_point)
print(point)

Catch bad JSON

Text that is not valid JSON raises JSONDecodeError. A try block can catch it and carry on.

import json

try:
    json.loads("{not json}")
except json.JSONDecodeError:
    print("That text is not valid JSON.")

Example

import json
from dataclasses import dataclass

@dataclass
class Book:
    title: str
    pages: int

def encode_book(obj):
    if isinstance(obj, Book):
        return {"title": obj.title, "pages": obj.pages, "kind": "book"}
    raise TypeError("Not a Book")

def decode_book(data):
    if data.get("kind") == "book":
        return Book(data["title"], data["pages"])
    return data

book = Book("Dune", 412)
text = json.dumps(book, default=encode_book, sort_keys=True)
print(text)
back = json.loads(text, object_hook=decode_book)
print(back)
print(back == book)

Key points

  • JSON object keys are always strings
  • A number key in a dict comes back as a string after a round trip
  • Round trip: save data and load it back again
  • Use default to tell dumps how to save an object it does not know
  • Use object_hook to turn each loaded dict into your own kind of object
  • JSONDecodeError: the error raised when text is not valid JSON
  • JSON spells its special values true, false and null
  • Python spells its special values True, False and None

In this example, Book is a dataclass with a title and a page count. dumps meets a Book and does not know it. The default function encode_book turns the Book into a plain dict. A kind field marks the dict as a book. The JSON text prints with sorted keys. loads then runs decode_book on the dict. The kind field tells it to build a Book again. The last line proves the round trip kept the data.

Note

A set is not valid JSON data on its own.

A default function can turn it into a list first.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Python Playground

Exercise

You load JSON that came from the dict {1: "one"}. What is the key in the loaded dict?