Python JSON Handling

Convert between JSON and Python with the json module, write readable files and validate what you parse.

Lesson 48 of 107Intermediate12 min read

JSON (JavaScript Object Notation) is the text format used by most web APIs and many configuration files. Python's built-in json module converts JSON text into dictionaries and lists, and back again, with four functions:

FunctionFromTo
json.loads(text)JSON stringPython object
json.dumps(obj)Python objectJSON string
json.load(file)JSON filePython object
json.dump(obj, file)Python objectJSON file

The s stands for string. This lesson covers the type mapping between JSON and Python, readable output, saving and loading files, and why parsed JSON still needs validating. The examples manage a camping trip checklist.

Data mapping

JSON has six kinds of value, and each maps to a Python type:

JSONPython
object {"a": 1}dict
array [1, 2]list
string "rope"str
number 3, 2.5int, float
true / falseTrue / False
nullNone
Python
import json
checklist = json.loads('{"camp": "Pine Bay", "nights": 2, "booked": true, "items": ["lamp"]}')
print(type(checklist), checklist["booked"], type(checklist["nights"]))
# <class 'dict'> True <class 'int'>

Going the other way, tuples are written as arrays, so they come back as lists. JSON object keys are always strings, so a dictionary with integer keys returns with string keys: {1: "a"} becomes {"1": "a"}. Sets, dates, Decimal values and your own class instances aren't supported by default, and dumps raises TypeError: Object of type set is not JSON serializable.

Strings and files

Use the s functions when the JSON is already in a string, for example an API response body, and the file functions when reading or writing a file:

Python
import json

checklist = {"camp": "Pine Bay", "items": ["lamp", "rope"]}

with open("checklist.json", "w", encoding="utf-8") as file:
    json.dump(checklist, file, indent=2)

with open("checklist.json", encoding="utf-8") as file:
    loaded = json.load(file)

print(loaded == checklist)   # True

A file must be opened in text mode with an encoding for these functions. Passing a file object to loads, or a string to load, is a common mix-up that fails with TypeError or AttributeError.

Invalid JSON raises json.JSONDecodeError, which includes the line and column of the problem. Typical causes are single quotes (JSON requires double quotes), trailing commas, comments, and Python's True/None spellings instead of true/null.

Formatting

By default, dumps produces compact, single-line text. Options make it easier to read or compare:

Python
data = {"camp": "Café du Lac", "items": ["lamp", "rope"], "nights": 2}
print(json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False))
Text
{
  "camp": "Café du Lac",
  "items": [
    "lamp",
    "rope"
  ],
  "nights": 2
}
  • indent=2 adds line breaks and indentation, ideal for files people will read and diffs in version control.
  • sort_keys=True writes keys alphabetically, so the same data always produces the same text.
  • ensure_ascii=False keeps characters like é readable. The default True writes them as escapes such as é, which are equally valid but harder to read. When using it with files, open them with encoding="utf-8".
  • separators=(",", ":") removes spaces for the smallest possible output.

These options change only the text. Loading any of these variants gives back the same Python data.

Validation

json.loads checks that text is syntactically valid JSON. It doesn't check that the data has the shape your program expects:

Python
import json

def load_checklist(text):
    data = json.loads(text)
    if not isinstance(data, dict):
        raise ValueError("checklist must be a JSON object")
    items = data.get("items")
    if not isinstance(items, list) or not all(isinstance(i, str) for i in items):
        raise ValueError("items must be a list of strings")
    return data

'"rope"', '[1, 2]' and '{"items": 5}' are all valid JSON, but none is a valid checklist. Data from users, files or other services should be checked before use, especially before it reaches a database or decides an action. For larger schemas, libraries such as jsonschema or Pydantic describe the expected shape declaratively.

The json module never executes code, so parsing untrusted JSON is safe in that sense. Never use eval() to parse JSON-like text.

Independent practice and a worked check

Task: validate a campsite checklist and write readable UTF-8 JSON without serialising unsupported objects.

Python
import json
from pathlib import Path

raw = '{"camp": "Lac d’Annecy", "items": ["tent", "stove"], "tags": ["lake"]}'
data = json.loads(raw)

if not isinstance(data.get("items"), list):
    raise ValueError("items must be a list")
data["items"].append("torch")

path = Path("camp.json")
path.write_text(json.dumps(data, indent=2, ensure_ascii=False), encoding="utf-8")
print(json.loads(path.read_text(encoding="utf-8"))["items"])   # ['tent', 'stove', 'torch']
path.unlink()

Tags were stored as a list, not a set, so dumps needs no default function. For the worked check, the function that decodes JSON from a string is json.loads.

Reference and next practice

The json module documentation covers every option, and json.org defines the format. Continue with CSV handling and web APIs. Practise with the Python exercises and Python quiz.

JSON Handling Summary

  • Data mapping: JSON objects map to dictionaries, arrays to lists and null to None.
  • Strings and files: loads and dumps work with strings while load and dump work with file objects.
  • Formatting: indent improves readability and ensure_ascii=False preserves readable Unicode characters.
  • Validation: Parsing checks JSON syntax but application rules still need validation.

JSON Handling Examples

Each example shows the code, the output it produces and what each part does.

Example 1

JSON Handling in a small program

Python
import json
text = '{"camp": "Pine Bay", "items": ["lamp", "rope"]}'
checklist = json.loads(text)
checklist["items"].append("map")
print(json.dumps(checklist, sort_keys=True))
Output
{"camp": "Pine Bay", "items": ["lamp", "rope", "map"]}
Line-by-line explanation
  1. Lines 2 to 3loads parses the JSON string into a dictionary containing a list.
  2. Line 4The result is ordinary Python data, so append() works.
  3. Line 5dumps turns it back into JSON text; sort_keys makes the key order predictable.
Example 2

Saving objects JSON doesn't support

Python
import json
from datetime import date

trip = {"camp": "Pine Bay", "start": date(2026, 7, 4), "tags": {"lake", "forest"}}

try:
    json.dumps(trip)
except TypeError as error:
    print("Error:", error)

def to_json(value):
    if isinstance(value, date):
        return value.isoformat()
    if isinstance(value, set):
        return sorted(value)
    raise TypeError(f"Cannot serialise {type(value).__name__}")

text = json.dumps(trip, default=to_json, sort_keys=True)
print(text)
back = json.loads(text)
print(type(back["start"]).__name__, back["tags"])
Output
Error: Object of type date is not JSON serializable
{"camp": "Pine Bay", "start": "2026-07-04", "tags": ["forest", "lake"]}
str ['forest', 'lake']
Line-by-line explanation
  1. Lines 6 to 9Without help, dumps fails on the first unsupported value.
  2. Lines 11 to 16The default function is called for each unsupported object and returns something JSON can store.
  3. Lines 19 to 20Loading gives back a string and a list: converting them to date and set again is up to your code.

Common JSON Handling Mistakes

Mixing up load and loads

Don't
with open("checklist.json", encoding="utf-8") as f:
    data = json.loads(f)          # TypeError: the JSON object must be str, bytes or bytearray
Do
with open("checklist.json", encoding="utf-8") as f:
    data = json.load(f)

loads takes a string; load takes a file object.

Writing Python syntax and calling it JSON

Don't
text = "{'camp': 'Pine Bay', 'booked': True}"
json.loads(text)                  # JSONDecodeError
Do
text = '{"camp": "Pine Bay", "booked": true}'
json.loads(text)

JSON requires double quotes and lower-case true, false and null. Generate JSON with json.dumps() instead of building it by hand.

JSON Handling in Real Projects

JSON in real Python projects

Web APIs send and receive JSON, and libraries such as requests offer response.json() as a shortcut for json.loads(response.text). Applications store settings in JSON files, and tools such as VS Code, npm and Jupyter notebooks use it for their configuration and data. Logging systems often write one JSON object per line ("JSON Lines") so logs can be searched and analysed.

Production code typically validates incoming JSON at the boundary, converts it into dataclasses or Pydantic models, and works with typed objects from then on. That keeps JSON's loose structure from spreading through the program.

JSON Handling FAQ

What is the difference between json.load and json.loads?

json.load(file) reads JSON from an open file object. json.loads(text) parses JSON from a string. Similarly, json.dump(obj, file) writes to a file and json.dumps(obj) returns a string.

How do I pretty-print JSON in Python?

Use json.dumps(data, indent=2). Add sort_keys=True for alphabetical keys and ensure_ascii=False to keep non-English characters readable. From the command line, python -m json.tool file.json pretty-prints a file.

Why do I get "Object of type X is not JSON serializable"?

JSON supports only dictionaries, lists, strings, numbers, booleans and null. Convert other values first, for example dates to ISO strings and sets to lists, or pass a default= function to dumps that does the conversion.

How do I fix JSONDecodeError: Expecting property name enclosed in double quotes?

The text uses single quotes or has a trailing comma, which JSON doesn't allow. If the data came from Python, it was probably written with str() instead of json.dumps(). Regenerate it with json.dumps() rather than editing quotes by hand.

Are JSON and Python dictionaries the same thing?

No. JSON is a text format; a dictionary is an in-memory Python object. They look similar, but JSON requires double-quoted string keys and uses true, false and null, while dictionaries accept any hashable key and use True, False and None.

JSON Handling Exercises

Answer each one and check it instantly. If you get stuck, open the hint before the answer.

Multiple choiceeasy

Which function decodes JSON from a string?

Multiple choicemedium

Which review concern is valid for json handling?

JSON Handling Quiz

2 questions to check what you learned.

  1. Which function decodes JSON from a string?

    • json.load
    • json.loads
    • Python ignores the distinction
  2. Which statement correctly describes formatting?

    • indent improves readability and ensure_ascii=False preserves readable Unicode characters.
    • Only the example output matters, not the rule
    • Every operation has the same behavior