JSON
JavaScript Object Notation — the universal format for sending and storing structured data.
1. Introduction
JSON (JavaScript Object Notation) is a text format for representing structured data. It looks almost identical to JavaScript object/array literals, but it is pure text — meaning it can be sent over a network, saved to a file, or stored in localStorage.
JSON is the standard data format for web APIs. When your JavaScript fetches data from a server, it almost always arrives as JSON text. When you send data to a server, you typically send JSON. Two built-in functions handle the conversion: JSON.stringify and JSON.parse.
2. Theory
2.1 JSON syntax rules
// Valid JSON — keys MUST be double-quoted strings
{
"name": "Alice",
"age": 28,
"active": true,
"score": null,
"tags": ["admin", "user"],
"address": {
"city": "London",
"country": "UK"
}
}
// JSON supports these types:
// string — "hello"
// number — 42, 3.14
// boolean — true, false
// null — null
// array — [1, "two", true]
// object — { "key": "value" }
// JSON does NOT support:
// undefined, functions, Date objects, Symbol, Infinity, NaN
2.2 JSON.stringify — object to string
const user = {
name: 'Alice',
age: 28,
active: true,
tags: ['admin', 'user']
};
// Basic stringify
const json = JSON.stringify(user);
console.log(json);
// '{"name":"Alice","age":28,"active":true,"tags":["admin","user"]}'
console.log(typeof json); // 'string'
// Pretty-print — second arg = replacer, third = indent spaces
const pretty = JSON.stringify(user, null, 2);
console.log(pretty);
// {
// "name": "Alice",
// "age": 28,
// "active": true,
// "tags": ["admin", "user"]
// }
// What stringify does to unsupported values
const tricky = {
fn: () => 'hello', // functions are omitted
undef: undefined, // undefined values are omitted
inf: Infinity, // becomes null
nan: NaN, // becomes null
date: new Date() // becomes ISO string (Date has toJSON method)
};
console.log(JSON.stringify(tricky));
// '{"inf":null,"nan":null,"date":"2026-06-01T..."}'
2.3 JSON.parse — string to object
const jsonString = '{"name":"Bob","age":34,"scores":[90,85,92]}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // 'Bob'
console.log(obj.scores[1]); // 85
console.log(typeof obj); // 'object'
// JSON.parse throws on invalid JSON!
try {
JSON.parse("{ name: 'Alice' }"); // invalid — keys not quoted
} catch (e) {
console.error('Invalid JSON:', e.message);
}
// Always wrap in try/catch when parsing untrusted input
function safeParse(str, fallback = null) {
try {
return JSON.parse(str);
} catch {
return fallback;
}
}
2.4 Using JSON with localStorage
// localStorage can only store strings — use JSON to store objects
const settings = { theme: 'dark', fontSize: 16, notifications: true };
// Save
localStorage.setItem('settings', JSON.stringify(settings));
// Load
const saved = localStorage.getItem('settings');
const loaded = saved ? JSON.parse(saved) : null;
console.log(loaded?.theme); // 'dark'
// Pattern: load with defaults
const DEFAULT_SETTINGS = { theme: 'light', fontSize: 14, notifications: true };
function getSettings() {
const raw = localStorage.getItem('settings');
return raw ? { ...DEFAULT_SETTINGS, ...JSON.parse(raw) } : { ...DEFAULT_SETTINGS };
}
function saveSettings(changes) {
const current = getSettings();
localStorage.setItem('settings', JSON.stringify({ ...current, ...changes }));
}
2.5 Deep clone with JSON (simple cases)
// Quick deep clone — works for plain data (no functions, no Date, no undefined)
const original = {
name: 'Alice',
address: { city: 'London', zip: 'SW1' },
tags: ['admin', 'user']
};
const clone = JSON.parse(JSON.stringify(original));
clone.address.city = 'Paris'; // modify clone
console.log(original.address.city); // 'London' — unaffected
// For modern environments, prefer structuredClone (no JSON limitations):
const clone2 = structuredClone(original);
2.6 JSON replacer — select/transform output
const user = {
name: 'Alice',
password: 'secret123', // should not be serialised!
age: 28,
role: 'admin'
};
// Array replacer — only include listed keys
const safe = JSON.stringify(user, ['name', 'age', 'role']);
console.log(safe); // '{"name":"Alice","age":28,"role":"admin"}'
// Function replacer — custom transform per key
const masked = JSON.stringify(user, (key, value) => {
if (key === 'password') return '[REDACTED]';
return value;
});
console.log(masked);
// '{"name":"Alice","password":"[REDACTED]","age":28,"role":"admin"}'
2.7 JSON.parse reviver — transform while parsing
// Reviver runs on every key-value pair during parsing
const jsonStr = '{"name":"Alice","createdAt":"2026-01-15T10:00:00.000Z","score":95}';
const result = JSON.parse(jsonStr, (key, value) => {
// Convert date strings back to Date objects
if (key === 'createdAt') return new Date(value);
return value;
});
console.log(result.createdAt instanceof Date); // true
console.log(result.createdAt.getFullYear()); // 2026
2.8 Fetching JSON from an API (preview)
// You'll learn fetch fully in Module 14 — here's the pattern
fetch('https://api.example.com/users/1')
.then(response => response.json()) // parse JSON response
.then(user => {
console.log(user.name);
console.log(user.email);
})
.catch(err => console.error('Failed:', err));
// response.json() is equivalent to:
// response.text().then(text => JSON.parse(text))
3. Real World Example
// Persistent notes app using localStorage + JSON
const STORAGE_KEY = 'notes-app-data';
function loadNotes() {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return [];
try {
return JSON.parse(raw);
} catch {
console.warn('Corrupt data — resetting');
localStorage.removeItem(STORAGE_KEY);
return [];
}
}
function saveNotes(notes) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(notes));
}
function addNote(title, body) {
const notes = loadNotes();
notes.push({
id: Date.now(),
title,
body,
createdAt: new Date().toISOString(),
pinned: false
});
saveNotes(notes);
}
function deleteNote(id) {
const notes = loadNotes().filter(n => n.id !== id);
saveNotes(notes);
}
function togglePin(id) {
const notes = loadNotes().map(n =>
n.id === id ? { ...n, pinned: !n.pinned } : n
);
saveNotes(notes);
}
// Usage
addNote('Shopping', 'Milk, eggs, bread');
addNote('Meeting', 'Call at 3pm');
console.log(loadNotes()); // [{id:..., title:'Shopping', ...}, {...}]
togglePin(loadNotes()[0].id);
console.log(loadNotes()[0].pinned); // true
4. Code Example
<textarea id="json-in" rows="8" placeholder="Paste JSON here"></textarea>
<button id="parse-btn">Parse JSON</button>
<button id="stringify-btn">Stringify Object</button>
<pre id="output"></pre>
<script>
const output = document.querySelector('#output');
const input = document.querySelector('#json-in');
const sampleObject = {
users: [
{ id: 1, name: 'Alice', role: 'admin' },
{ id: 2, name: 'Bob', role: 'user' }
],
meta: { total: 2, page: 1 }
};
document.querySelector('#stringify-btn').addEventListener('click', () => {
const json = JSON.stringify(sampleObject, null, 2);
output.textContent = json;
input.value = json; // populate textarea for editing
});
document.querySelector('#parse-btn').addEventListener('click', () => {
try {
const parsed = JSON.parse(input.value);
// Display type info
const info = Object.entries(parsed).map(([k, v]) =>
`${k}: ${Array.isArray(v) ? `Array(${v.length})` : typeof v}`
);
output.textContent = 'Parsed successfully!\n\nKeys:\n' + info.join('\n');
} catch (e) {
output.textContent = 'Parse error: ' + e.message;
}
});
</script>
5. Code Breakdown
JSON.stringify with indentation
The third argument 2 adds two-space indentation to the output. The second argument null means "no replacer — include all properties". Together they produce human-readable formatted JSON.
try/catch around JSON.parse
If the input is invalid JSON, JSON.parse throws a SyntaxError. Wrapping in try/catch lets us display a friendly error message rather than crashing the whole page.
Type detection in the output
Array.isArray(v) is used before typeof because typeof [] returns 'object' — not helpful. This is a classic JavaScript quirk worth remembering.
6. Common Mistakes
Mistake 1 — Storing objects in localStorage without stringify
localStorage.setItem('user', { name: 'Alice' });
// Stored as: "[object Object]" — useless!
// Fix
localStorage.setItem('user', JSON.stringify({ name: 'Alice' }));
const user = JSON.parse(localStorage.getItem('user'));
Mistake 2 — Not handling parse errors
// Crashes if localStorage has corrupt or empty data
const data = JSON.parse(localStorage.getItem('data')); // may throw!
// Fix
const raw = localStorage.getItem('data');
const data = raw ? JSON.parse(raw) : {}; // but still no error handling
// Better:
function loadData() {
try { return JSON.parse(localStorage.getItem('data')) ?? {}; }
catch { return {}; }
}
Mistake 3 — Expecting Date objects to survive JSON round-trip
const obj = { date: new Date() };
const json = JSON.stringify(obj);
const back = JSON.parse(json);
console.log(back.date instanceof Date); // false — it's a string!
// Fix — use a reviver
const back2 = JSON.parse(json, (key, val) =>
key === 'date' ? new Date(val) : val
);
Mistake 4 — Using single quotes in JSON text
// Invalid JSON — single quotes
JSON.parse("{ 'name': 'Alice' }"); // SyntaxError
// Valid JSON requires double quotes
JSON.parse('{ "name": "Alice" }'); // works
7. Best Practices
- Always wrap JSON.parse in try/catch when parsing data from external sources.
- Use null, 2 as stringify arguments when you want human-readable output for debugging.
- Never use JSON round-trip for objects containing functions, Dates, or undefined — use structuredClone for deep cloning.
- Use a replacer to exclude sensitive fields (passwords, tokens) when stringifying user data.
- Provide defaults when loading from localStorage — the item may be missing on first run.
- Validate the parsed shape before trusting it — type-check key fields if the data comes from external sources.
8. Practice Exercise
- Create a JavaScript object representing a product catalogue (array of product objects). Stringify it, log it, then parse it back and confirm the result is equal to the original by accessing specific properties.
- Write a
saveToStorage(key, data)andloadFromStorage(key, defaultValue)pair that handles JSON conversion and errors transparently. - Fetch the public JSON from
https://jsonplaceholder.typicode.com/todos/1in your browser console usingfetch().then(r => r.json()).then(data => console.log(data)). Inspect the shape.
9. Assignment
Build a "Local Todo App" with full persistence.
- All todos stored in localStorage as JSON:
[{ id, text, done, createdAt }]. - On page load, parse stored todos and render them.
- Add, toggle done, and delete — save to localStorage after every change.
- Show a "Data" panel that displays the raw JSON from localStorage, updating after every change.
- Add an "Export" button that copies the JSON to the clipboard (
navigator.clipboard.writeText). - Add an "Import" button that reads JSON from a textarea input and replaces the current todos.
Deliverable: One HTML file.
10. Interview Questions
- What is JSON?
JavaScript Object Notation — a text-based format for representing structured data. It supports strings, numbers, booleans, null, arrays, and objects. It is the standard format for web API communication. - What is the difference between JSON.stringify and JSON.parse?
JSON.stringify converts a JavaScript value to a JSON string. JSON.parse converts a JSON string back to a JavaScript value. Stringify goes JS → text; parse goes text → JS. - Why must JSON keys be double-quoted?
JSON is a strict text format defined by RFC 8259 — it requires double-quoted string keys. JavaScript object literals allow unquoted keys and single quotes, but that syntax is not valid JSON. - What happens to functions and undefined values in JSON.stringify?
They are silently omitted from the output (when they are property values). If undefined or a function is at the top level, stringify returns undefined itself. Infinity and NaN become null. - How do you persist data in the browser across page refreshes?
Use localStorage. Since localStorage only stores strings, use JSON.stringify before saving and JSON.parse after loading. Always wrap the parse in try/catch and provide default values for missing data.
11. Additional Resources
- MDN — JSON
- MDN — JSON.stringify()
- MDN — JSON.parse()
- json.org — the official JSON specification