Topic 6.6
JSON
In one line
JSON is a text format for data that looks like JavaScript objects; JSON.stringify turns values into JSON text and JSON.parse turns the text back into values, with rules about what survives, plus options for pretty printing and for filtering or reviving values.
Think of it like this
Posting a toy to a friend. You can't post the assembled toy, so you take it apart and write instructions (stringify), send the paper, and your friend builds it again from the instructions (parse). Some parts can't be written down, like the batteries' charge (functions) or a sticker that says "made today" (a date becomes plain text). What arrives is a copy, never the original toy.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- JSON
- JavaScript Object Notation: a strict text format for data, used by APIs, files and storage.
- Serialise
- Turn a value into text (or bytes) so it can be sent or saved.
JSON.stringifyserialises. - Parse
- Read text and build values from it.
JSON.parseparses JSON text. - Replacer
- The second argument of
stringify: a list of keys to keep or a function that changes values. - Reviver
- The second argument of
parse: a function that can change each value as it is rebuilt. - toJSON
- A method an object can have to choose what
stringifywrites for it. - Circular reference
- An object that contains itself somewhere inside, like
a.self = a. JSON can't write it. - ISO string
- A standard date text like
2024-01-15T10:30:00.000Z.
Step by step
01Turn data into text
stringify gives a string. Keys get double quotes, and the result is one compact line.
const user = { name: "Asha", age: 14 };
const text = JSON.stringify(user);
console.log(text); // {"name":"Asha","age":14}02Turn text back into data
parse builds a brand new object from the text. It has the same data, but it isn't the original object.
const copy = JSON.parse(text);
console.log(copy.name); // Asha
console.log(copy === user); // false03Save and load from localStorage
localStorage can only store strings, so you stringify on the way in and parse on the way out. Guard the parse: stored text can be missing (null) or broken. (In Hectal's preview, storage lasts only until the next Run.)
localStorage.setItem("settings", JSON.stringify({ theme: "dark" }));
function loadSettings() {
try {
return JSON.parse(localStorage.getItem("settings")) ?? {};
} catch {
return {};
}
}04Read JSON from a server
fetch (Topic 11.2) gets a response; response.json() reads the body and parses it as JSON for you. If the body isn't valid JSON it rejects with a SyntaxError, just like JSON.parse.
const response = await fetch("/api/user/7");
const user = await response.json();
console.log(user.name);05Control what gets written
A toJSON method decides an object's JSON form. Here a money object writes itself as a simple string.
const price = {
amount: 150,
currency: "INR",
toJSON() {
return this.amount + " " + this.currency;
},
};
console.log(JSON.stringify({ price })); // {"price":"150 INR"}Try it yourself
- 1
Pretty print the round trip
In the first example, change the stringify to
JSON.stringify(student, null, 2). Predict the new value oftext.length: bigger or smaller? Run to check. - 2
Add a toJSON
In the second example, add
toJSON() { return "tricky object"; }totricky. Predict the first line. - 3
Hide more keys
In the third example, change the replacer to also drop
total. Predict the number of lines in the pretty output.
Code & diagrams
The JSON text is a string. Parsing it creates completely new objects and arrays, so nothing is shared with the original.
Preview
Console
Nothing logged yet.Expected console output
{"name":"Asha","age":14,"subjects":["maths","art"],"monitor":false,"pet":null}
string 78
art
false false`missing` and `greet` vanished; inside the array they became `null` so the positions stay the same. The date came back as a string, and the `Set` lost its contents.
Preview
Console
Nothing logged yet.Expected console output
{"when":"2024-01-15T10:30:00.000Z","notANumber":null,"big":null,"tags":{},"list":[1,null,null]}
string
TypeError: Do not know how to serialize a BigInt
TypeErrorThe array replacer kept two keys. The function replacer dropped `cardNumber`, and `2` indented the result (see the page). The reviver turned the date string back into a `Date`.
Preview
Console
Nothing logged yet.Expected console output
{"id":42,"total":155}
5
true 10Unquoted keys, single quotes, trailing commas and empty text are all invalid JSON. A bare number like `42` is valid. The exact message wording differs between browsers; this one is Chrome's.
Preview
Console
Nothing logged yet.Expected console output
parsed: { ok: true }
bad JSON: SyntaxError
bad JSON: SyntaxError
bad JSON: SyntaxError
parsed: 42
Expected property name or '}' in JSON at position 1 (line 1 column 2)Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Parse something that isn't JSON
Call JSON.parse(localStorage.getItem("missing-key") || "undefined"), or any text that isn't JSON, without try...catch.
Break #2
Expect a Date back
Stringify an object with a Date, parse it, and call back.when.getFullYear().
Myth vs fact
Myth
JSON is the same as a JavaScript object.
Fact
JSON is text. It looks like an object literal but is stricter (double quotes only, no comments, no trailing commas, no functions or undefined). You must parse it to get an object.
Myth
JSON.parse(JSON.stringify(x)) is a good deep copy.
Fact
It loses dates (become strings), undefined, functions, Map, Set, NaN and Infinity, and throws on BigInt and cycles. Use structuredClone (Topic 6.5) for deep copies.
Myth
JSON.parse is dangerous, like eval.
Fact
JSON.parse only builds data and never runs code. eval runs code; never use it to read data.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Since ES2019, JSON is a true subset of JavaScript: the characters U+2028 and U+2029 are allowed in JS strings too.
JSON.stringifyalso escapes lone surrogates (well-formedJSON.stringify, ES2019), so output is always valid UTF-8. - ▸
The reviver runs bottom-up: children are revived before their parent, and finally the root with key
"". The replacer runs top-down, starting with the root under key"". Both receivethisas the object holding the current key. - ▸
Large numbers lose precision: JSON numbers are read as 64-bit floats, so IDs above
Number.MAX_SAFE_INTEGER(9007199254740991) change silently. APIs send big IDs as strings for this reason. A newer feature (JSON.rawJSON, plus the original source text passed to revivers) allows exact handling; check browser support before relying on it. - ▸
Embedding JSON inside an HTML
<script>needs<escaped (as\u003c), or a value containing</script>would end the script element: a classic cross-site scripting (XSS) risk. Server frameworks provide safe helpers for this.
Remember this
- 1
JSON (JavaScript Object Notation) is a text format for sending and saving data. It looks like JavaScript, but it's stricter: keys must be in double quotes, strings use double quotes, there are no comments and no trailing commas. A JSON value can only be an object, an array, a string, a number,
true,falseornull. It's the standard language for web APIs, config files andlocalStorage, and every programming language can read it. (See/system-designfor how APIs use it.) - 2
**
JSON.stringify(value)turns a value into a JSON string.JSON.parse(text)** reads a JSON string and builds new values from it. A round trip,JSON.parse(JSON.stringify(obj)), gives you a new object with the same data, never the same object. If the text isn't valid JSON,JSON.parsethrows a SyntaxError, so wrap it intry...catchwhenever the text comes from outside (a server, storage, user input). - 3
Some values can't be written in JSON, and
stringifyhandles each in a fixed way. Properties whose value isundefined, a function or a symbol are left out of objects (and becomenullinside arrays).NaNandInfinitybecomenull. ADatebecomes an ISO string (it has atoJSONmethod), and parsing does not turn it back into a date.MapandSetbecome{}(their data is lost). A BigInt throws a TypeError, and so does an object that contains itself (a circular reference). - 4
Pretty printing: the third argument of
stringifyis the indentation, a number of spaces (up to 10) or a string like"\t":JSON.stringify(data, null, 2). The output gets new lines and indentation, perfect for showing data on a page, logging or saving readable files. Without it, JSON is one compact line, best for sending over the network. - 5
The second argument of
stringifyis a replacer. An array of keys acts as an allowlist:JSON.stringify(user, ["id", "name"])keeps only those keys. A function(key, value) => ...is called for every property: return the value to keep it, a different value to change it, orundefinedto drop it (great for hiding passwords).JSON.parsetakes a reviver function in the same shape, called for each value as it's built, which is how you turn date strings back intoDateobjects. - 6
Any object can control its own JSON with a **
toJSON()method**:stringifycalls it and uses what it returns. That's howDateworks. Always preferJSON.parseoverevalfor reading data:evalruns any code inside the text, which is a security hole;JSON.parseonly ever builds data.
Explain it without notes
What is JSON, and how is it different from a JavaScript object literal?
What happens to undefined, functions, NaN, Date, Map and BigInt in JSON.stringify?
How do the replacer and reviver arguments work? Give a use for each.
How should you safely read JSON from localStorage or an API?
Practice
Stringify { title: "Notes", pages: 40, draft: undefined } with two-space indentation and log it. Which key is missing and why?
Write safeParse(text, fallback) that returns the parsed value, or fallback if the text isn't valid JSON. Test it with '[1,2]' and '[1,2'.
Stringify { user: "asha", password: "secret", token: "abc" } so that password and token are written as "***".
Trade-offs
- ↔
JSON is readable, universal and built in, but it has no dates, binary data, big integers or comments; formats like MessagePack or Protocol Buffers are smaller and typed but need libraries and aren't human-readable.
- ↔
Pretty-printed JSON is easy to read and diff but bigger; send compact JSON over the network (compression shrinks the difference) and pretty-print for humans.
Done when you can
I can convert objects to JSON text and back.
I know what
stringifydoes withundefined, functions,NaN, dates,Mapand BigInt.I can pretty-print JSON with indentation.
I can use a replacer to hide or change values and a reviver to restore dates.
I always wrap
JSON.parseof outside data intry...catch.