Home Tutorials Standard Library

Standard Library

JSON Handling in Python: Serialising, Deserialising, and Edge Cases

Pyford Notes July 1, 2026 7 min read
Key points
  • json.loads(s) parses a string; json.load(f) reads from a file object.
  • json.dumps(obj, indent=2) produces human-readable output; omit indent for compact wire format.
  • json only handles str, int, float, bool, None, list, dict — pass a custom default= function for other types.
  • JSON keys are always strings; Python dicts allow any hashable key, so round-trip fidelity requires care.

How do I parse a JSON string?

Use json.loads() (the s stands for string). It converts a JSON-formatted string into a Python object and raises json.JSONDecodeError — a subclass of ValueError — if the input is malformed:

import json

raw = '{"name": "Alice", "age": 30, "active": true}'
data = json.loads(raw)

print(data["name"])    # Alice
print(type(data))      # <class 'dict'>
print(data["active"])  # True  (JSON true becomes Python True)

JSON's null maps to Python's None, true/false map to True/False, JSON arrays become Python lists, and JSON objects become Python dicts.

How do I read JSON from a file?

Use json.load() (no s) with a file object. Open the file in text mode with UTF-8 encoding to handle international characters correctly:

import json

with open("config.json", encoding="utf-8") as fh:
    config = json.load(fh)

print(config["database"]["host"])

If you already have the file contents as a string (for example, from a network response), use json.loads() instead. The distinction is: load accepts a file-like object; loads accepts a string.

How do I serialise Python objects to JSON?

Use json.dumps() to produce a JSON string, or json.dump() to write directly to a file:

import json

data = {"name": "Bob", "scores": [95, 87, 100], "passed": True}

# To a string
s = json.dumps(data)
print(s)
# {"name": "Bob", "scores": [95, 87, 100], "passed": true}

# To a file
with open("output.json", "w", encoding="utf-8") as fh:
    json.dump(data, fh)

By default, keys are sorted alphabetically only if you pass sort_keys=True. Without it they follow Python dict insertion order (Python 3.7+).

How do I pretty-print JSON?

Pass indent to either dumps or dump. An integer value sets the number of spaces per indentation level; "\t" uses a tab character:

import json

payload = {"user": {"id": 42, "roles": ["admin", "editor"]}}

print(json.dumps(payload, indent=2))
# {
#   "user": {
#     "id": 42,
#     "roles": [
#       "admin",
#       "editor"
#     ]
#   }
# }

For wire formats and APIs, omit indent entirely and also pass separators=(",", ":") to strip the spaces after colons and commas, producing the most compact output possible.

How do I serialise types json doesn't know about?

The json module raises TypeError if it encounters a type it cannot handle, such as a datetime, a set, or a custom class instance. Supply a default function that converts unknown objects to a serialisable form:

import json
from datetime import datetime, date

def json_default(obj):
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    if isinstance(obj, set):
        return sorted(obj)  # convert to a sorted list
    raise TypeError(f"Object of type {type(obj)} is not JSON serialisable")

data = {
    "created": datetime(2026, 7, 1, 9, 30),
    "tags": {"python", "tutorial"},
}

print(json.dumps(data, default=json_default, indent=2))
# {
#   "created": "2026-07-01T09:30:00",
#   "tags": ["python", "tutorial"]
# }

Alternatively, subclass json.JSONEncoder and override its default method, then pass cls=MyEncoder to dumps. The functional approach is simpler for most cases.

What are the common pitfalls?

Several mismatches between JSON and Python catch developers off-guard:

  • Key type loss. JSON only has string keys. A Python dict with integer keys such as {1: "one"} serialises to {"1": "one"}. On round-trip the key becomes the string "1", not the integer 1.
  • Tuple becomes list. Python (1, 2, 3) serialises to JSON [1, 2, 3] and deserialises back as a list. If tuple identity matters, do not use plain JSON.
  • Float precision. JSON has no decimal type. Values like 0.1 + 0.2 may not round-trip exactly. Use Decimal with a custom encoder if precision matters.
  • Non-ASCII characters. By default, json.dumps escapes non-ASCII characters to \uXXXX. Pass ensure_ascii=False to keep them as-is (and open the file with encoding="utf-8").
print(json.dumps({"city": "Munich"}, ensure_ascii=False))
# {"city": "Munich"}   (not {"city": "Munich"} with escaped chars)

How do I handle large JSON files efficiently?

The standard json module loads the entire document into memory. For files that are too large to fit comfortably in RAM, consider the ijson third-party library, which provides a streaming parser:

# pip install ijson
import ijson

with open("huge.json", "rb") as fh:
    for record in ijson.items(fh, "results.item"):
        process(record)  # handles one record at a time

For files up to a few hundred megabytes, json.load() with a buffered file object is usually fast enough. The standard library's json module is implemented in C (via _json) in CPython, so parsing speed is not often the bottleneck; memory is.

Another practical approach for moderately large files is to split them into newline-delimited JSON (NDJSON), where each line is a complete JSON object. You can then process line by line with the standard library:

with open("records.ndjson", encoding="utf-8") as fh:
    for line in fh:
        record = json.loads(line)
        process(record)