{"id":"data-processing-json","slug":"json-files","title":"Reading JSON files","text":"**JSON** (JavaScript Object Notation) is the most common format for data that programs pass to each other: web services send their answers in it, and many apps save their settings in it. To a Python programmer it looks familiar, because it's written almost exactly like lists and dictionaries:\n\n```file games.json\n[\n    {\n        \"name\": \"Chess\",\n        \"players\": 2,\n        \"tags\": [\"strategy\", \"classic\"]\n    },\n    {\n        \"name\": \"Uno\",\n        \"players\": 4,\n        \"tags\": [\"cards\", \"family\"]\n    }\n]\n```\n\nThe standard library's **`json`** module turns JSON text into Python data. `json.loads(text)` (load from a string) takes the text and gives the list or dictionary it describes:\n\n```python\nimport json\n\nwith open(\"games.json\") as file:\n    text = file.read()\n\ngames = json.loads(text)\nprint(games)\n```\n\n```output\n[{'name': 'Chess', 'players': 2, 'tags': ['strategy', 'classic']}, {'name': 'Uno', 'players': 4, 'tags': ['cards', 'family']}]\n```\n\nWhat comes out is an ordinary list of dictionaries, so everything you know about lists and dictionaries works on it. A JSON file can also be one dictionary, with lists and dictionaries nested inside it as deep as needed. Numbers become integers or floats, ready for calculating:\n\n```file team.json\n{\n    \"team\": \"Owls\",\n    \"players\": [\n        {\"name\": \"Ada\", \"goals\": 3},\n        {\"name\": \"Ben\", \"goals\": 5}\n    ]\n}\n```\n\n```python\nimport json\n\nwith open(\"team.json\") as file:\n    team = json.loads(file.read())\n\nprint(\"Team:\", team[\"team\"])\ntotal = 0\nfor player in team[\"players\"]:\n    print(f\"{player['name']} scored {player['goals']}\")\n    total += player[\"goals\"]\nprint(\"Goals in all:\", total)\n```\n\n```output\nTeam: Owls\nAda scored 3\nBen scored 5\nGoals in all: 8\n```\n\n(Inside the f-string the keys are in single quotes, so they stand out from the f-string's own double quotes. Python 3.12 and later accept double quotes there too, but older versions don't, so you'll see single quotes in most code.) The module also has `json.load(file)`, without the s, which reads the open file directly.\n\n### JSON is not quite Python\n\nA few things are written differently in JSON. Strings always have double quotes. True and false are `true` and `false` in small letters, and the missing value is `null`. The module translates them:\n\n```pycon\n>>> import json\n>>> json.loads('{\"done\": true, \"score\": 9.5, \"note\": null}')\n{'done': True, 'score': 9.5, 'note': None}\n```\n\nJSON's rules are strict, and text that breaks them stops the program with a `JSONDecodeError` that says where the problem is. Single quotes are the usual culprit:\n\n```python\nimport json\n\ndata = json.loads(\"{'name': 'Ada'}\")\n```\n\n```crash\nTraceback (most recent call last):\n  File \"main.py\", line 3, in <module>\n    data = json.loads(\"{'name': 'Ada'}\")\njson.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)\n```\n","task":"What does this program print? Type each line it prints.\n","kind":"predict","seed":"import json\n\ntext = '{\"name\": \"Ada\", \"languages\": [\"Python\", \"C\"], \"active\": true}'\nperson = json.loads(text)\nprint(person[\"name\"])\nprint(person[\"languages\"][1])\nprint(person[\"active\"])\nprint(len(person))\n","solution":"import json\n\ntext = '{\"name\": \"Ada\", \"languages\": [\"Python\", \"C\"], \"active\": true}'\nperson = json.loads(text)\nprint(person[\"name\"])\nprint(person[\"languages\"][1])\nprint(person[\"active\"])\nprint(len(person))\n","hints":["`json.loads` gives a dictionary with three keys: `name`, `languages` and `active`.","`person[\"languages\"]` is a list, and index 1 is its second item.","JSON's `true` becomes Python's `True`, and the length of a dictionary is its number of keys."],"tests":[{"name":"Output","stdin":"","expect":"Ada\nC\nTrue\n3","hidden":false}],"require":[],"forbid":[],"lines":["Ada","C","True","3"],"more":[{"task":"The file `books.json` has a list of books, each with a title and a number of pages:\n\n```text\n[\n    {\"title\": \"Python Pocket Guide\", \"pages\": 210},\n    {\"title\": \"Data in Practice\", \"pages\": 345}\n]\n```\n\nWrite a function `total_pages(filename)` that reads such a file with the `json` module and returns the total number of pages of all the books. For the file above it returns `555`.\n","kind":"code","seed":"import json\n\n\ndef total_pages(filename):\n    pass\n\n\nif __name__ == \"__main__\":\n    print(total_pages(\"books.json\"))\n","solution":"import json\n\n\ndef total_pages(filename):\n    with open(filename) as file:\n        books = json.loads(file.read())\n    total = 0\n    for book in books:\n        total += book[\"pages\"]\n    return total\n\n\nif __name__ == \"__main__\":\n    print(total_pages(\"books.json\"))\n","hints":["Read the whole file with `file.read()` and give the text to `json.loads`. You get a list of dictionaries.","Loop over the list and add up `book[\"pages\"]`. The pages are already integers."],"tests":[{"name":"total_pages(\"books.json\")","stdin":"","call":"total_pages(\"books.json\")\n","files":{"books.json":"[\n    {\"title\": \"Python Pocket Guide\", \"pages\": 210},\n    {\"title\": \"Data in Practice\", \"pages\": 345}\n]\n"},"expect":"555","hidden":false,"returns":true},{"name":"total_pages(\"shelf.json\")","stdin":"","call":"total_pages(\"shelf.json\")\n","files":{"shelf.json":"[\n    {\"title\": \"Short Poems\", \"pages\": 64},\n    {\"title\": \"The Long Road\", \"pages\": 512},\n    {\"title\": \"Maps of Nowhere\", \"pages\": 98}\n]\n"},"expect":"674","hidden":true,"returns":true},{"name":"total_pages(\"empty.json\")","stdin":"","call":"total_pages(\"empty.json\")\n","files":{"empty.json":"[]\n"},"expect":"0","hidden":true,"returns":true}],"require":[{"pattern":"json\\.loads?\\s*\\(","message":"Read the file with the `json` module."}],"forbid":[]},{"task":"A JSON file has a list of recipes, each with a name, a list of ingredients and the minutes it takes:\n\n```text\n[\n    {\"name\": \"Pancakes\", \"ingredients\": [\"flour\", \"milk\", \"eggs\", \"butter\", \"salt\"], \"minutes\": 20},\n    {\"name\": \"Tomato soup\", \"ingredients\": [\"tomatoes\", \"onion\", \"garlic\", \"stock\", \"cream\", \"basil\"], \"minutes\": 35}\n]\n```\n\nWrite a program that asks for the name of the file, reads it with the `json` module and prints one line for each recipe, as in the example:\n\n```\nFile: recipes.json\nPancakes: 5 ingredients, 20 minutes\nTomato soup: 6 ingredients, 35 minutes\n```\n","kind":"code","seed":"import json\n","solution":"import json\n\nfilename = input(\"File: \")\nwith open(filename) as file:\n    recipes = json.loads(file.read())\n\nfor recipe in recipes:\n    count = len(recipe[\"ingredients\"])\n    print(f\"{recipe['name']}: {count} ingredients, {recipe['minutes']} minutes\")\n","hints":["Ask for the file's name with `input(\"File: \")` and open the file of that name.","Each recipe is a dictionary. The number of ingredients is the length of the list `recipe[\"ingredients\"]`.","In an f-string in double quotes, write the keys in single quotes, so they stand out: `recipe['name']`."],"tests":[{"name":"Test 1","stdin":"recipes.json\n","files":{"recipes.json":"[\n    {\"name\": \"Pancakes\", \"ingredients\": [\"flour\", \"milk\", \"eggs\", \"butter\", \"salt\"], \"minutes\": 20},\n    {\"name\": \"Tomato soup\", \"ingredients\": [\"tomatoes\", \"onion\", \"garlic\", \"stock\", \"cream\", \"basil\"], \"minutes\": 35}\n]\n"},"expect":"File: recipes.json\nPancakes: 5 ingredients, 20 minutes\nTomato soup: 6 ingredients, 35 minutes","hidden":false},{"name":"Test 2","stdin":"dinner.json\n","files":{"dinner.json":"[\n    {\"name\": \"Omelette\", \"ingredients\": [\"eggs\", \"salt\"], \"minutes\": 10},\n    {\"name\": \"Pasta\", \"ingredients\": [\"pasta\", \"tomatoes\", \"cheese\"], \"minutes\": 25},\n    {\"name\": \"Salad\", \"ingredients\": [\"lettuce\", \"cucumber\", \"oil\", \"lemon\"], \"minutes\": 15}\n]\n"},"expect":"File: dinner.json\nOmelette: 2 ingredients, 10 minutes\nPasta: 3 ingredients, 25 minutes\nSalad: 4 ingredients, 15 minutes","hidden":true}],"require":[{"pattern":"json\\.loads?\\s*\\(","message":"Read the file with the `json` module."}],"forbid":[]}]}