To read a JSON file in Python, we open it with open() and pass the file object to json.load(), which returns the data as dictionaries, lists, strings, numbers, booleans and None. Config files and API exports are read this way, using only the standard library.
import json
with open("recipes.json", encoding="utf-8") as f:
recipes = json.load(f)
In this article, we read a sample file, access nested values, compare json.load() with json.loads(), handle missing and broken files, and read JSON Lines. The examples run on Python 3.14, and the complete files are in the read-json-file folder on GitHub.
1. Reading a File into Python Objects
The sample file recipes.json holds an array of two objects, so json.load() returns a list of dictionaries.
[
{"name": "Pancakes", "minutes": 20, "vegan": false, "tags": ["breakfast", "sweet"]},
{"name": "Lentil soup", "minutes": 45, "vegan": true, "tags": ["dinner"]}
]
kind = type(recipes) # <class 'list'>
first = recipes[0]["name"] # 'Pancakes'
vegan = recipes[1]["vegan"] # True, JSON true becomes Python True
tag = recipes[0]["tags"][1] # 'sweet'
servings = recipes[0].get("servings", 2) # 2, the key is missing
We always pass encoding=”utf-8″ to open(), because the default encoding depends on the operating system and breaks non-ASCII text on Windows. The get() method returns a default instead of raising KeyError for optional keys.
| JSON | Python |
|---|---|
| object | dict |
| array | list |
| string | str |
| number (integer / real) | int / float |
| true / false | True / False |
| null | None |
2. json.load() vs json.loads()
The two functions differ only in their input. The json.load() function reads from a file object, while json.loads() parses a string, such as an HTTP response body or text we already read.
json.load(fp, **kwargs) -> object # fp is a file object, text or binary
json.loads(s, **kwargs) -> object # s is a str, bytes or bytearray
Both return the top-level JSON value as a Python object, converted with the rules in the table in section 1. Invalid JSON makes both raise json.JSONDecodeError.
from pathlib import Path
text = Path("recipes.json").read_text(encoding="utf-8")
same = json.loads(text) == recipes # True
3. Handling Missing and Broken Files
A missing file raises FileNotFoundError, and invalid JSON raises json.JSONDecodeError, which carries the line and column of the problem. The file broken.json holds one line with a trailing comma, {“name”: “Pancakes”, “minutes”: 20,}.
def load_json(path):
try:
with open(path, encoding="utf-8") as f:
return json.load(f)
except FileNotFoundError:
print("No such file:", path)
except json.JSONDecodeError as e:
print(f"Bad JSON in {path}: {e.msg} at line {e.lineno}, column {e.colno}")
return None
load_json("missing.json")
load_json("broken.json")
No such file: missing.json
Bad JSON in broken.json: Illegal trailing comma before end of object at line 1, column 35
We can check a file from the terminal before loading it in code. Since Python 3.14, python -m json broken.json prints the same error, and python -m json.tool still works on older versions.
4. Reading JSON Lines Files
Logs and data exports often use JSON Lines, with one JSON object per line and no surrounding array. Calling json.load() on such a file fails, so we parse each line with json.loads(). The file recipes.jsonl holds the two recipes, one per line.
with open("recipes.jsonl", encoding="utf-8") as f:
rows = [json.loads(line) for line in f if line.strip()]
names = [r["name"] for r in rows] # ['Pancakes', 'Lentil soup']
5. Loading a Config File with Defaults
Many applications read settings from a JSON file and fall back to defaults for keys the file leaves out. The dictionary merge operator | (Python 3.9+) does that in one expression, with the values from the file winning.
DEFAULTS = {"port": 8080, "debug": False, "theme": "light"}
def load_config(path):
try:
with open(path, encoding="utf-8") as f:
return DEFAULTS | json.load(f)
except FileNotFoundError:
return dict(DEFAULTS)
config = load_config("settings.json") # {'port': 9000, 'debug': False, 'theme': 'dark'}
The file settings.json holds only {“port”: 9000, “theme”: “dark”}, so debug comes from the defaults. A missing file returns a copy of the defaults, which keeps the program running on a fresh install.
6. JSON from URLs, Comments and Large Files
Real projects read JSON from more places than a local file, and not every file is small or strict JSON.
6.1. How do we read JSON from a URL?
The response of urllib.request.urlopen() is a file-like object, so json.load(response) works on it. Many projects use the third-party requests library and call response.json().
6.2. Can a JSON file contain comments?
No. Standard JSON allows no comments, and json.load() raises JSONDecodeError on // or #. Config files that need comments often use TOML, which Python reads with the built-in tomllib module.
6.3. How do we read a JSON file that is too large for memory?
The json module always loads the whole document. For huge files, we store the data as JSON Lines and read it line by line, or use a streaming parser such as the third-party ijson.
7. Conclusion
Reading a JSON file takes open() with encoding=”utf-8″ and one call to json.load(). In real code we also catch FileNotFoundError and json.JSONDecodeError, and we parse JSON Lines line by line. To go the other way, see writing JSON to a file.
8. References
Happy Learning !!