Recommended Free Tools
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
To save a Python list or dictionary to a JSON file, open the file in text mode with encoding="utf-8", then pass the file object to json.dump(). To load it later, open the file the same way and call json.load(). A JSON file holds one document, so a group of records belongs in one list that you write once. Most failures people hit with JSON files come from three things: encoding, writing several documents to one file, and input that is not valid JSON. The sections below cover each one, along with the values Python cannot convert to JSON on its own.
Write a Python value to a JSON file
- Import the standard-library
jsonmodule. No installation is needed. - Open the target file in write mode (
"w") withencoding="utf-8". - Call
json.dump(value, f), passing your Python object and the file object. Addindent=2if you want the file to be easy to read.
import json
record = {"name": "Ada", "active": True, "skills": ["math", "logic"]}
with open("record.json", "w", encoding="utf-8") as f:
json.dump(record, f, ensure_ascii=False, indent=2)
json.dump() writes text, so the file must be opened in text mode. Passing a file opened with "wb" raises a TypeError. The Python 3.13 tutorial’s “Input and Output” section sets the encoding expectation directly: “JSON files must be encoded in UTF-8.” It also recommends passing encoding="utf-8" whenever you open a text file for JSON.
Read the file back
import json
with open("record.json", "r", encoding="utf-8") as f:
loaded = json.load(f)
print(loaded["name"]) # Ada
Reading a file with json.load() returns ordinary Python objects. The conversion follows a fixed mapping:
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstall| JSON value | Python value after json.load() |
|---|---|
object {} |
dict |
array [] |
list |
| string | str |
| number (integer form) | int |
| number (with fraction or exponent) | float |
true / false |
True / False |
null |
None |
Choose between dump, dumps, load and loads
The module has a pair of functions for files and a pair for strings. Use the file functions when the data goes to or comes from disk, and the string functions when you already have the JSON text in memory, such as a value from a network response.
#1 Best Overall
| Function | Accepts | Result |
|---|---|---|
json.dump(value, fp) |
Python value and a writable text file object | Writes JSON text to fp; returns nothing |
json.dumps(value) |
Python value | Returns a str containing JSON |
json.load(fp) |
Readable text file object | Parses one JSON document and returns a Python value |
json.loads(s) |
str, bytes or bytearray |
Parses one JSON document from the string and returns a Python value |
The “s” at the end of dumps and loads means “string,” not “several.” Neither pair handles more than one document per call.
Format the output and handle non-ASCII text
- Readable output:
indent=2puts each element on its own line. Leave it out for the most compact file. - Compact output:
separators=(",", ":")removes the spaces after commas and colons. This is useful when file size matters more than readability. - Stable diffs:
sort_keys=Truewrites object keys in alphabetical order, so two saves of the same data produce identical files. - Non-ASCII text:
ensure_asciidefaults toTrue, which escapes characters outside ASCII. For example,{"city": "São Paulo"}is written as{"city": "São Paulo"}. Settingensure_ascii=Falsewrites the character itself, which works well with a UTF-8 file.
Know what changes during a round trip
JSON object keys must be strings. When you dump a dictionary whose keys are numbers, Python converts those keys to strings, so the loaded dictionary is different from the original. For example, {1: "a"} is written as {"1": "a"}, and json.load() returns {"1": "a"}. Tuples are written as JSON arrays and come back as lists. If your code depends on the exact key or container types, convert them after loading.
Why repeated dump calls produce an invalid file
A common mistake is calling json.dump() several times on the same file object, expecting each call to add a separate record. The Python json reference states the rule plainly: “Unlike pickle and marshal, JSON is not a framed protocol, so trying to serialize multiple objects with repeated calls to dump() using the same fp will result in an invalid JSON file.”
Rank #2
import json
with open("log.json", "w", encoding="utf-8") as f:
json.dump({"id": 1}, f)
json.dump({"id": 2}, f)
The file now contains {"id": 1}{"id": 2}. That is two JSON values with nothing separating them, so a later json.load() call fails with a JSONDecodeError that reports extra data after the first document. Calling json.dump() does not add separators, so the output is not a stream of valid documents. Choose one of the two patterns below.
Store the records in one list
If the records belong together, put them in a list and dump the list once. This is the simplest option and the most widely supported.
import json
events = [{"id": 1}, {"id": 2}]
with open("log.json", "w", encoding="utf-8") as f:
json.dump(events, f, indent=2)
with open("log.json", "r", encoding="utf-8") as f:
events = json.load(f)
The whole file must then be read into memory before you can work with any single record, so very large collections are better handled with the next approach.
Use JSON Lines for independent records
JSON Lines stores one JSON value per line. You serialize each record with json.dumps() and add a newline. Each line can then be parsed on its own, which suits logs and streams of records. The newline is your separator, so the file format must be documented wherever the file is used.
import json
records = [{"id": 1}, {"id": 2}]
with open("log.jsonl", "w", encoding="utf-8") as f:
for rec in records:
f.write(json.dumps(rec) + "n")
with open("log.jsonl", "r", encoding="utf-8") as f:
loaded = [json.loads(line) for line in f if line.strip()]
Do not use json.load() on a JSON Lines file. It expects a single document and will fail on the second line.
Validate and pretty-print from the command line
The json module can be run from the command line to check a file or reformat it. The current Python 3.14 reference documents python -m json for this purpose and keeps python -m json.tool working for backwards compatibility.
python -m json record.json
python -m json --json-lines log.jsonl
- With a valid file, the formatted JSON is printed to standard output.
- With an invalid file, the command reports a parsing error instead of printing the output.
--json-linesparses each line as a separate JSON object, so it is the right check for files created with the JSON Lines pattern above.- The tool can also read from standard input and write to a file, and it accepts options for sorting keys and setting indentation.
Handle invalid and unexpected input
Invalid JSON raises json.JSONDecodeError, which is a subclass of ValueError. The exception provides the position of the problem, so you can report it to the user or log it:
import json
try:
with open("record.json", "r", encoding="utf-8") as f:
data = json.load(f)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e.msg} at line {e.lineno}, column {e.colno}")
Do not treat every exception as a JSON problem. A missing file raises FileNotFoundError, and a file that is not valid UTF-8 raises UnicodeDecodeError. Those errors are not JSONDecodeError, so catch them separately.
Do these 3 things before closing this tab:
1Repair Windows errors before they cause bigger problems2Scan for outdated or missing drivers - takes under a minute3Clear out junk files and repair common Windows errorsWhen the data comes from an untrusted source, limit its size before parsing. The Python reference warns that malicious input can consume a large amount of CPU and memory during parsing. The following check reads at most a fixed number of bytes:
Best Value
import json
MAX_BYTES = 1_000_000
with open("upload.json", "rb") as f:
raw = f.read(MAX_BYTES + 1)
if len(raw) > MAX_BYTES:
raise ValueError("JSON input exceeds the allowed size")
data = json.loads(raw)
Reading the file in binary mode and passing the bytes to json.loads() lets the module detect the encoding from the bytes. The size limit is a resource-exhaustion control; it does not validate the structure of the data, so keep your normal checks on the loaded values.
Serialize values that JSON does not support
JSON supports only objects, arrays, strings, numbers, booleans and null. Passing a datetime, a set or a custom class to json.dump() raises a TypeError with the message Object of type X is not JSON serializable. The module will not convert these values for you. Supply a conversion function through the default argument, and have it raise an error for anything it does not handle:
import json
from datetime import date
def encode_extra(obj):
if isinstance(obj, date):
return obj.isoformat()
raise TypeError(f"Object of type {type(obj).__name__} is not JSON serializable")
payload = {"created": date(2026, 10, 9)}
with open("event.json", "w", encoding="utf-8") as f:
json.dump(payload, f, default=encode_extra, indent=2)
The date is stored as the string "2026-10-09". After json.load() it comes back as a string, so your code must convert it to a date again. Keep the encoder and the decoding step together in one place so the two stay consistent.
JSON or pickle
The Python tutorial contrasts JSON with pickle. Both can save Python data to a file, but they suit different jobs.
| Criterion | JSON | pickle |
|---|---|---|
| Interoperability | Designed for data interchange between applications and languages | Specific to Python |
| Data types | Objects, arrays, strings, numbers, booleans and null | Most Python objects, including custom classes |
| Custom classes | Need a conversion function (default) and a matching decode step |
Serialized directly |
| Untrusted input | Parsing can use a lot of CPU and memory, so limit input size; the format does not execute code during loading | Deserializing untrusted data is unsafe because crafted input can execute code |
| Human-readable | Yes, as plain text | No, binary format |
Use JSON when another program, language or service needs to read the file. Use pickle only for Python-to-Python data that you created yourself and trust, and never load a pickle file from a source you do not control.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

