How to Read and Write JSON Files: A Practical Guide
Almost every application, script, or configuration system you’ll ever build needs to read data from a JSON file, write data to one, or both. The good news is that this is one of the more straightforward tasks in programming — every major language has built-in or near-universal support for JSON — but there are a handful of practical details that trip people up, especially around encoding, error handling, and file structure. This guide walks through the core concepts and shows practical examples across the languages you’re most likely to be using.
What “Reading and Writing JSON” Actually Means
At a basic level, there are two directions of conversion happening whenever you work with JSON files:
- Parsing (reading) — converting the raw text content of a JSON file into a native data structure your programming language can actually work with (an object, dictionary, list, etc.)
- Serializing (writing) — converting a native data structure back into JSON-formatted text, which can then be saved to a file or sent over a network
These two operations are often called different things depending on the language — parse/stringify in JavaScript, load/dump in Python — but the underlying concept is identical everywhere.
Reading a JSON File
Python:
import json
with open("data.json", "r") as file:
data = json.load(file)
print(data["name"])
JavaScript (Node.js):
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('data.json', 'utf8'));
console.log(data.name);
JavaScript (browser, via fetch):
fetch('data.json')
.then(response => response.json())
.then(data => console.log(data.name));
In every case, the pattern is the same: get the raw text content of the file, then hand it to a JSON parser, which converts it into a usable object.

Writing a JSON File
Python:
import json
data = {"name": "Kings Tools", "free": True}
with open("output.json", "w") as file:
json.dump(data, file, indent=2)
The indent=2 argument is optional but worth using — it writes the file in readable, indented form rather than a single dense line, which makes it far easier to inspect or debug later.
JavaScript (Node.js):
const fs = require('fs');
const data = { name: "Kings Tools", free: true };
fs.writeFileSync('output.json', JSON.stringify(data, null, 2));
Here, null, 2 in JSON.stringify() plays the same role as Python’s indent=2 — the 2 sets the indentation width, and null (the second argument) is a replacer function you’re choosing not to use.
Handling Errors When Reading JSON
Real-world JSON files aren’t always well-formed — a file might be empty, corrupted, or written by something that produced invalid output. Reading a file without handling this case is one of the most common sources of unhandled crashes in applications that work with JSON.
Python:
import json
try:
with open("data.json", "r") as file:
data = json.load(file)
except json.JSONDecodeError as e:
print(f"Invalid JSON: {e}")
except FileNotFoundError:
print("File not found")
JavaScript:
try {
const data = JSON.parse(fs.readFileSync('data.json', 'utf8'));
} catch (e) {
console.error('Invalid JSON:', e.message);
}
Both languages raise a specific, catchable error when parsing fails, and both include a message describing what went wrong — though as covered in our guide to common JSON errors, that message is often just a raw character position rather than a plain description, which is exactly the kind of thing worth checking with a formatter before you spend time debugging in code.
File Encoding Matters
JSON files should generally be UTF-8 encoded — this is the default assumption most JSON parsers make, and the JSON specification itself is built around Unicode. If you’re generating JSON files from a system that defaults to a different encoding (this comes up more than people expect on Windows systems, for example), you can end up with a file that looks fine in one text editor but fails to parse correctly elsewhere. Explicitly specifying UTF-8 encoding when opening files, as shown in the Python examples above, avoids this entirely.
Working with Large JSON Files
Loading an entire JSON file into memory at once, the way json.load() and JSON.parse() do, works fine for the vast majority of use cases — configuration files, API responses, small-to-medium datasets. But for genuinely large files (hundreds of megabytes or more), loading everything into memory at once can be slow or even crash the process.
For those cases, most languages offer streaming JSON parsers that process the file incrementally rather than all at once — ijson in Python, or JSONStream in Node.js, for example. These are worth reaching for specifically when file size becomes a real constraint, not by default, since streaming parsers are more complex to work with than a straightforward load/parse call.
A Note on NDJSON for Line-by-Line Data
If you’re working with logs, streaming data, or very large datasets structured as a sequence of independent records, standard JSON (one big document) isn’t always the best fit — appending to a JSON array file, for instance, requires rewriting the entire file. NDJSON (Newline Delimited JSON), where each line is its own self-contained JSON object, is often a better structure for this kind of use case, since new records can simply be appended to the end of the file without touching what’s already there.
A Common Mistake: “Appending” to a JSON File
A pattern that trips up a lot of people new to working with JSON files: trying to append a new entry to an existing JSON array by simply adding text to the end of the file, the way you might append a line to a plain text log:
{"log": "first entry"}
{"log": "second entry"}
This looks reasonable but isn’t valid JSON — a file can only contain one top-level JSON value, not several concatenated objects. To properly add an entry to a JSON array file, you need to read the existing file, parse it, modify the resulting data structure in memory, and then write the entire file back out:
import json
with open("data.json", "r") as file:
data = json.load(file)
data.append({"log": "new entry"})
with open("data.json", "w") as file:
json.dump(data, file, indent=2)
This read-modify-write cycle is the correct approach for standard JSON files. If you find yourself appending frequently — logging events as they happen, for instance — that’s usually a sign NDJSON (covered above) is a better fit than a single JSON array file, since NDJSON genuinely does support appending a new line without touching the rest of the file.
Frequently Asked Questions
Can a JSON file have a file extension other than .json?
Technically yes — the file’s content is what determines whether it’s valid JSON, not its extension. But using .json is a strong convention that tells other developers, editors, and tools what to expect, and most JSON-aware tooling (like syntax highlighting or schema validation in code editors) relies on that extension to activate.
Do I need to close the file manually after reading or writing?
In Python, using with open(...) as file: handles closing the file automatically when the block ends, even if an error occurs partway through — this is the recommended pattern over manually calling .close(). In Node.js, the synchronous methods shown above (readFileSync/writeFileSync) don’t require manual closing at all.
Why does my written JSON file look different from what I expected?
This is almost always an indentation or key-ordering difference. Most serializers preserve the order keys were added to the object, but this isn’t guaranteed by the JSON specification itself — if key order matters for your use case, don’t rely on it being preserved without checking your specific language’s behavior.
Validating Before You Write Code Around It
Before building parsing logic around a JSON file — especially one you didn’t generate yourself, like a third-party API response or a file handed to you by someone else — it’s worth confirming the file is actually valid JSON first. Pasting the contents into our JSON Formatter and Validator takes a few seconds and tells you immediately whether the file is well-formed, and exactly where it breaks if it isn’t, before you spend time writing error-handling code around a problem that turns out to be a simple typo in the source file.
Summary
Reading and writing JSON files comes down to two operations — parsing text into a native data structure, and serializing a data structure back into text — available as built-in functionality in virtually every programming language. The practical details that matter most in real-world use are handling parse errors gracefully, using UTF-8 encoding consistently, and reaching for a streaming parser only when file size genuinely requires it. Get those basics right, and reading and writing JSON becomes one of the most reliable parts of any codebase.
