W3docs

Python JSON

Learn how to use Python's built-in json module to encode, decode, read, and write JSON data, with practical examples and error handling.

Python's built-in json module lets you convert between Python objects and JSON text with just two functions in most cases. This chapter covers everything you need: data-type mapping, dumps/loads for strings, dump/load for files, pretty-printing, error handling, and custom serialization.

What is JSON?

JSON (JavaScript Object Notation) is a lightweight, text-based data-interchange format. It is human-readable, language-independent, and the default format for most web APIs.

A JSON document is built from two structures:

  • Objects — unordered collections of key/value pairs enclosed in {}. Keys are always strings in double quotes.
  • Arrays — ordered sequences of values enclosed in [].

Example JSON document:

{
  "name": "John Doe",
  "age": 30,
  "city": "New York",
  "hobbies": ["reading", "traveling", "photography"],
  "active": true,
  "score": null
}

Python–JSON type mapping

When you encode or decode JSON, Python's json module converts types according to this table:

Python typeJSON typePython type (after decode)
dictobject {}dict
list, tuplearray []list
strstring ""str
int, floatnumberint or float
True / Falsetrue / falsebool
NonenullNone

Note that tuples become JSON arrays and come back as Python lists after decoding.

Encoding Python objects to JSON strings

json.dumps() (dump-string) serializes a Python object into a JSON-formatted string.

Basic encoding

python— editable, runs on the server

Output:

{"name": "John Doe", "age": 30, "city": "New York", "hobbies": ["reading", "traveling", "photography"]}

Pretty-printing with indent and sort_keys

By default json.dumps() produces a compact single-line string. Pass indent to produce human-readable output, and sort_keys=True to sort dictionary keys alphabetically:

import json

person = {
    "name": "John Doe",
    "age": 30,
    "city": "New York",
    "hobbies": ["reading", "traveling", "photography"]
}

print(json.dumps(person, indent=2, sort_keys=True))

Output:

{
  "age": 30,
  "city": "New York",
  "hobbies": [
    "reading",
    "traveling",
    "photography"
  ],
  "name": "John Doe"
}

Use indent=2 or indent=4 when writing configuration files or debugging API responses — it makes nested structures easy to read.

Decoding JSON strings to Python objects

json.loads() (load-string) parses a JSON string and returns the equivalent Python object.

Basic decoding

python— editable, runs on the server

Output:

{'name': 'John Doe', 'age': 30, 'city': 'New York', 'hobbies': ['reading', 'traveling', 'photography']}
<class 'dict'>
John Doe

The decoded value is a regular Python dictionary, so you can access its keys with [] or .get(), iterate it with for, and so on.

Handling malformed JSON

If the input is not valid JSON, json.loads() raises json.JSONDecodeError. Always catch it when working with data from external sources:

import json

raw = '{"name": "Alice", "age":}'   # invalid — missing value

try:
    data = json.loads(raw)
except json.JSONDecodeError as e:
    print(f"Invalid JSON: {e}")

Output:

Invalid JSON: Expecting value: line 1 column 24 (char 23)

See the Python try/except chapter for a full guide to exception handling.

Working with nested JSON

JSON objects can contain other objects and arrays to any depth. Access nested values using chained [] notation:

python— editable, runs on the server

Output:

John
traveling

For deeply nested structures, consider using .get() with a default to avoid KeyError on missing keys:

city = person.get("address", {}).get("city", "unknown")

The nested dictionaries chapter covers patterns for working with deeply nested Python dicts.

Reading and writing JSON files

json.dump() writes to a file object, and json.load() reads from one. These are the file counterparts of dumps/loads.

Writing JSON to a file

import json

data = {"name": "Alice", "scores": [95, 87, 92]}

with open("data.json", "w") as f:
    json.dump(data, f, indent=2)

This creates data.json with pretty-printed content. Using with open(...) ensures the file is closed automatically — see Python file handling for details.

Reading JSON from a file

import json

with open("data.json") as f:
    data = json.load(f)

print(data)
print(data["scores"])

Output (assuming the file written above):

{'name': 'Alice', 'scores': [95, 87, 92]}
[95, 87, 92]

Full round-trip example

import json

# Write
config = {"host": "localhost", "port": 5432, "debug": False}
with open("config.json", "w") as f:
    json.dump(config, f, indent=2)

# Read back
with open("config.json") as f:
    loaded = json.load(f)

print(loaded["port"])   # 5432
print(type(loaded["port"]))   # <class 'int'>

Fetching JSON from a web API

In most projects you'll decode JSON that comes from an HTTP response. The popular requests library makes this straightforward:

import requests

response = requests.get("https://jsonplaceholder.typicode.com/todos/1")
response.raise_for_status()   # raises an error for 4xx/5xx responses
data = response.json()        # equivalent to json.loads(response.text)

print(data["title"])
print(data["completed"])

response.json() calls json.loads() internally. Always call raise_for_status() before parsing so a failed request doesn't silently return an error body.

Custom serialization with default

The json module cannot encode arbitrary Python objects (like datetime.date) by default. Pass a default callable or a custom JSONEncoder subclass to handle them:

import json
import datetime

class DateEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.date):
            return obj.isoformat()
        return super().default(obj)

event = {"name": "Conference", "date": datetime.date(2024, 1, 15)}
print(json.dumps(event, cls=DateEncoder))

Output:

{"name": "Conference", "date": "2024-01-15"}

The date object is converted to its ISO 8601 string before serialization.

Custom deserialization with object_hook

object_hook is called for every JSON object (dict) as it is decoded. You can use it to transform data on the fly — for example, converting string values to the right Python types:

import json

def as_record(d):
    """Convert age field from string to int if present."""
    if "age" in d:
        d["age"] = int(d["age"])
    return d

raw = '{"name": "Bob", "age": "25"}'
person = json.loads(raw, object_hook=as_record)
print(person)
print(type(person["age"]))   # <class 'int'>

Output:

{'name': 'Bob', 'age': 25}
<class 'int'>

Quick reference

FunctionDirectionSource/target
json.dumps(obj)Python → JSONreturns a str
json.loads(s)JSON → Pythonreads from a str
json.dump(obj, f)Python → JSONwrites to a file
json.load(f)JSON → Pythonreads from a file

Key optional parameters:

  • indent=2 — pretty-print with 2-space indentation
  • sort_keys=True — sort dictionary keys alphabetically
  • cls=MyEncoder — use a custom JSONEncoder subclass
  • object_hook=fn — transform each decoded object dict

Practice

Practice
Which Python function converts a Python dictionary to a JSON-formatted string?
Which Python function converts a Python dictionary to a JSON-formatted string?
Was this page helpful?