Vocabulary for this course

The terms the chapters assume, in plain words. You can open this from any chapter.

argument
A value you put inside the parentheses when you call a function, which the function then receives.
block
A group of lines that belong together under one opening line, such as the body of an if or a loop.
branch
A path a program can take when a condition sends it one way or another.
call site
The place in the code where a function is called, as opposed to where it is defined.
class
A template that bundles data with the code that works on it; each copy has its own values.
dictionary
A collection of key and value pairs, where a key is used to look up its value.
exception
An error that stops the normal flow of a program unless some code catches it.
function
A named piece of code that does one job and can be called whenever you need it.
function object
A function treated as a value, so it can be stored in a variable, passed to another function, or returned.
indentation
The spaces at the start of a line that show which block it belongs to.
integer
A whole number with no decimal point, such as 3, 0 or -12.
list
An ordered collection of values that you can grow, shrink and reach into by position.
parameter
A name in a function definition that stands for the value the caller will supply.
return value
The result a function hands back to the code that called it.
scope
The part of a program where a name can be used; a name made inside a function is not visible outside it.
traceback
The report Python prints when a program fails, listing the calls that led to the error.
variable
A name that refers to a value, so you can use that value again later or change it.

Python from the ground up

Working with JSON

The data format of the web, and the one module that turns Python values into it and back again.

Part 13 of 15

Intermediate15 min readBack to Courses

Text that looks like Python

JSON is a way of writing lists and dictionaries as text. It looks almost exactly like Python for the same values — {"name": "Ada", "languages": ["English", "French"]} — and that likeness is why it is the format config files, APIs and saved data all speak.

The json module turns your values into that text and back: json.dumps(data) gives a string, and json.loads(text) gives your values again. The s on the end of each name is for "string".

There and back

import json

recipe = {"name": "Flatbread", "minutes": 25, "steps": ["mix", "rest", "bake"]}

text = json.dumps(recipe, indent=2)
print(text)

loaded = json.loads(text)
print(loaded["name"])

Files and the world outside

With a file, the pair is json.dump(data, file) and json.load(file) — the same conversion, aimed at a file instead of a string. That is how a program keeps its settings, its notes or its saved games between runs.

The same two functions are what you use with every API you will ever call: a request comes back as JSON text, loads turns it into dictionaries and lists, and your program works with it like any other data.

Two rules JSON keeps

JSON has no comments, and its strings always use double quotes. That is the whole difference you will notice: a Python dictionary written with single quotes is not valid JSON text, which is exactly why the module writes it for you.

indent=2 makes the text readable for a person; without it you get one long line, which is what machines prefer.

Save the recipe as JSON

Turn the dictionary into readable JSON text, save it to recipe.json, then read the file back and print the recipe’s name from the loaded data.

Output
Click "Run" to execute your code

The text is the boundary

A dictionary in memory and a JSON document are different things that happen to look alike. The dictionary can hold a set, a function or a date; the document can hold only the six JSON types. dumps is the crossing point, and everything that cannot cross has to be converted first.

That is why the module raises TypeError instead of inventing a representation. A silent guess would produce a file that loads into something different from what you saved, and the difference would surface much later. A loud refusal at the boundary is the kinder behaviour.

Reading JSON you did not write

Text from an API or a file may be missing keys, hold null, or nest differently than you expected. loads only checks that the text is valid JSON; it does not check that the shape matches your program. The first lookup of a missing key raises KeyError, which is the moment the assumption becomes visible.

Use .get with a default for values that may be absent, and check the type before using it. A number that arrives as text will fail when you add to it, and the error will point at the arithmetic rather than at the document that supplied the string.

When a value refuses to convert — a set, a date, a custom object — the module says so plainly. Saving those means turning them into something JSON has, like a string or a list.

JSON has fewer types than Python

JSON understands objects, arrays, strings, numbers, booleans and null — and nothing else. A Python tuple becomes an array, a set cannot be written at all, and a date is not a JSON type. Converting is therefore lossy at the edges: (1, 2) comes back as [1, 2], and True comes back as true, which Python reads as True again.

The module handles the common shapes for you. When a value has no JSON equivalent, it raises TypeError rather than guessing; the fix is to convert that value yourself, usually with str(...) or by writing its parts as a dictionary.

The shape survives, the types may not

A round trip preserves the shape — dictionaries stay dictionaries, lists stay lists, nesting stays nested. It does not preserve Python types that JSON lacks: a tuple returns as a list, and an integer key in a dictionary returns as text.

That last one catches people out: {1: "one"} becomes {"1": "one"}. If the key matters, convert it back with int(key) after loading, or store the number as a value instead.

Three JSON mistakes: single-quoted Python text is not JSON, so json.loads("{'name': 'Ada'}") fails; a trailing comma is allowed in Python but not in JSON; and json.dump writes to a file while json.dumps returns a string — swapping them sends a file object where text was expected, or the reverse.

Try these next

  • Dump a list of three names with and without indent=2 and compare the text.
  • Load a JSON string containing true, false and null, and print the Python values and their types.
  • Try to dump a set, read the error, then convert it to a list and dump it again.

Settings with a default

Save the settings to settings.json as readable JSON, load them back, and print the theme. Then add a language key to the dictionary and run again — the fallback should stop being used.

Output
Click "Run" to execute your code

Check what you learned

One question at a time. A wrong answer explains itself — take another go.

Part 1 of 5

What does json.dumps(data) give back?