The format almost every API speaks
JSON (JavaScript Object Notation) is a plain text format for sending data between programs. Almost every web API sends and receives it.
You will read a JSON answer, write a JSON request and check that data is valid before you trust it.
By the end of this lesson
Note
I can read, write and check JSON.
Key words
| Word | What it means | Example |
|---|---|---|
| JSON | a text format made of objects and arrays | |
| Object | curly brackets holding named fields | {"id": 1} |
| Array | square brackets holding an ordered list | [1, 2, 3] |
| Field | one name and its value inside an object | "year": 2004 |
| Type | the kind of a value: text, number, true or false | |
| Validation | checking data follows the rules before using it |
The cells students fill in are left blank in this preview.
JSON has two shapes and six value kinds
An object is curly brackets holding fields. Each field is a name in double quotes, a colon and a value. An array is square brackets holding values in order.
A value is text in double quotes, a number, true or false, null for nothing, an object or an array. Objects and arrays can sit inside each other.
Worked example: the six kinds of value
| Kind | Written like | Example of a book field |
|---|---|---|
| Text | Double quotes | "title": "Night Trains" |
| Number | Digits, no quotes | "year": 2011 |
| Boolean | true or false, no quotes | "available": true |
| Null | null, meaning no value | "author": null |
| Object | Curly brackets | "publisher": {"name": "Tidal Press"} |
| Array | Square brackets | "tags": ["travel", "rail"] |
The cells students fill in are left blank in this preview.
Read JSON in Python
import json
text = '{"id": 2, "title": "Rivers of Europe", "available": false}'
book = json.loads(text)
print(book["title"])
print(type(book["available"]))
# Change the title in the text, then press Run again.
print(json.dumps(book))
Parsing turns JSON text into data
JSON arrives as text. Parsing turns it into the program's own lists and dictionaries. In Python json.loads parses and json.dumps turns data back into text.
After parsing, an object is a dictionary, so book["title"] reads one field, and an array is a list.
Make a catalogue and read one book
Run the next two commands to make a books table. Then ask the API for one book. The answer is an array that holds one object, because a request for rows always answers with an array.
Give psql the password
export PGPASSWORD=shopShown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Make the books table
psql -h db -U shop -c "drop table if exists books; create table books (id serial primary key, title text not null, year int not null, available boolean not null default true); insert into books (title, year, available) values ('Maps of the Sea', 1998, true), ('Rivers of Europe', 2004, false), ('Night Trains', 2011, true); notify pgrst, 'reload schema'"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Read one book as JSON
curl -s -w "\n" "http://api:3000/books?id=eq.2"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Ask for one field only
curl -s -w "\n" "http://api:3000/books?id=eq.2&select=title"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
A common mistake
Warning
Many people think that if JSON parses, the data is fine. Parsing only proves the text is well formed. {"title": 5, "year": "soon"} is valid JSON and is a nonsense book. Check the fields and their types yourself.
Validate data before you trust it
Validation checks that data follows the rules. For each field ask three things. Is it there? Is it the right type? Is it in range?
Report every problem at once, so a client fixes them in one go. In this lab the database is the last line of defence, and it refuses a bad row with a 400.
Worked example: checks for a book
| Field | Must be | Example that fails |
|---|---|---|
| title | Present, and text that is not empty | {"year": 2004} or {"title": ""} |
| year | A whole number from 1450 to 2100 | {"year": "soon"} or {"year": 3000} |
| available | true or false, and may be left out | {"available": "yes"} |
The cells students fill in are left blank in this preview.
Send text where a number belongs
curl -s -o /dev/null -w "%{http_code}\n" -X POST -H "Content-Type: application/json" -d '{"title":"Tide Tables","year":"soon"}' "http://api:3000/books"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Leave out the title
curl -s -o /dev/null -w "%{http_code}\n" -X POST -H "Content-Type: application/json" -d '{"year":2019}' "http://api:3000/books"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.
Send text that is not JSON at all
curl -s -o /dev/null -w "%{http_code}\n" -X POST -H "Content-Type: application/json" -d 'this is not json' "http://api:3000/books"Shown here, not run. Create a free account to press Run and type it into your own lab’s terminal.