JSON stands for JavaScript Object Notation. It is a lightweight, text-based data-interchange format that is easy for humans to read and write, and easy for machines to parse and generate.
JSON is language-independent but uses conventions familiar from the C family of languages (JavaScript, C, C++, Java, Python, etc.). It is widely used for transmitting data between a server and a web application.
{
"name": "Alice",
"age": 30,
"isAdmin": false,
"scores": [95, 87, 92]
}
JSON supports exactly six data types:
- String — text in double quotes:
"hello" - Number — integer or floating-point:
42,3.14 - Boolean —
trueorfalse - Null —
null - Array — ordered list:
[1, "two", true] - Object — key/value pairs:
{"key": "value"}
Note: JSON does not support undefined, functions, dates as a native type, or comments.
- Data is in name/value pairs separated by a colon.
- Keys must be strings wrapped in double quotes — single quotes are not allowed.
- Data items are separated by commas.
- Objects are enclosed in curly braces
{ }. - Arrays are enclosed in square brackets
[ ]. - Trailing commas are not allowed.
- Comments are not allowed.
// ✗ INVALID — single quotes, trailing comma, comment
{ 'name': 'Bob', "age": 25, }
// ✓ VALID
{ "name": "Bob", "age": 25 }
A JSON object is an unordered collection of key/value pairs enclosed in { }. Keys are always strings and must be unique.
A JSON array is an ordered list of values enclosed in [ ]. Values can be of any JSON type and duplicates are allowed.
// Object
{ "city": "Paris", "country": "France" }
// Array
["Paris", "London", "Berlin"]
// Array of objects
[
{ "city": "Paris", "pop": 2161000 },
{ "city": "London", "pop": 8982000 }
]
Yes. JSON values can be nested to any depth — an object can contain arrays, arrays can contain objects, and so on.
{
"user": {
"name": "Alice",
"address": {
"street": "123 Main St",
"city": "Boston"
},
"roles": ["admin", "editor"]
}
}
| Feature | JSON | XML |
|---|---|---|
| Syntax | Concise key/value pairs | Verbose opening/closing tags |
| Readability | Easier for humans | More verbose |
| Data types | Supports arrays natively | No native array type |
| Comments | Not supported | Supported |
| Parsing | Built-in in JS (JSON.parse) | Requires an XML parser |
| File size | Smaller | Larger due to tag overhead |
JSON is preferred for web APIs because:
- It is natively supported by JavaScript — no extra library needed to parse it.
- It produces smaller payloads, which means faster network transmission.
- It maps directly to JavaScript objects and arrays, making it easy to work with in the browser.
- REST APIs universally use JSON as the standard response format.
<!-- XML — verbose -->
<person>
<name>Alice</name>
<age>30</age>
</person>
{ "name": "Alice", "age": 30 }
JSON.parse() takes a JSON string and converts it into a JavaScript object (or array, number, boolean, etc.). It throws a SyntaxError if the string is not valid JSON.
const jsonString = '{"name":"Alice","age":30}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "Alice"
console.log(obj.age); // 30
// Throws SyntaxError:
JSON.parse("{ name: 'Alice' }"); // keys must be in double quotes
JSON.stringify() converts a JavaScript value into a JSON string. It accepts two optional parameters: a replacer (filter/transform values) and a space (indent for pretty-printing).
const obj = { name: "Alice", age: 30, active: true };
// Basic
JSON.stringify(obj);
// '{"name":"Alice","age":30,"active":true}'
// Pretty-print with 2-space indent
JSON.stringify(obj, null, 2);
// {
// "name": "Alice",
// "age": 30,
// "active": true
// }
JSON.stringify() silently drops or converts certain values:
undefined— omitted from objects, converted tonullin arrays.- Functions — omitted from objects, converted to
nullin arrays. Symbol— omitted.NaNandInfinity— serialized asnull.Dateobjects — converted to their ISO string representation.
JSON.stringify({
a: undefined, // omitted
b: function() {}, // omitted
c: NaN, // null
d: new Date() // "2026-06-09T..."
});
// '{"c":null,"d":"2026-06-09T..."}'
The replacer can be an array of keys to include, or a function that receives each key and value and returns what to include in the output.
const user = { name: "Alice", age: 30, password: "secret" };
// Array replacer — whitelist keys
JSON.stringify(user, ["name", "age"]);
// '{"name":"Alice","age":30}'
// Function replacer — omit password
JSON.stringify(user, (key, value) => {
if (key === "password") return undefined;
return value;
});
// '{"name":"Alice","age":30}'
The optional reviver function is called on every key/value pair after parsing. It lets you transform values — for example, converting ISO date strings back to Date objects.
const json = '{"name":"Alice","created":"2026-01-15T10:00:00.000Z"}';
const obj = JSON.parse(json, (key, value) => {
if (key === "created") return new Date(value);
return value;
});
console.log(obj.created instanceof Date); // true
After parsing, a JSON object becomes a plain JavaScript object. You can access values using dot notation or bracket notation.
const data = JSON.parse('{"user":{"name":"Alice","scores":[95,87]}}');
// Dot notation
console.log(data.user.name); // "Alice"
// Bracket notation
console.log(data["user"]["name"]); // "Alice"
// Array index
console.log(data.user.scores[0]); // 95
A common trick is to stringify then parse an object to get a deep clone — no references to the original.
const original = { a: 1, b: { c: 2 } };
const clone = JSON.parse(JSON.stringify(original));
clone.b.c = 99;
console.log(original.b.c); // 2 — original untouched
Limitation: This does not copy functions, undefined, Symbol, or Date objects accurately. For production use consider structuredClone().
const json = '[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]';
const users = JSON.parse(json);
// forEach
users.forEach(user => console.log(user.name));
// map — transform to array of names
const names = users.map(u => u.name);
// ["Alice", "Bob"]
// for...of
for (const user of users) {
console.log(user.id, user.name);
}
If an object has a toJSON() method, JSON.stringify() calls it automatically and serializes its return value instead of the object itself. This lets you control exactly what gets serialized.
const user = {
name: "Alice",
password: "secret",
toJSON() {
return { name: this.name }; // exclude password
}
};
JSON.stringify(user);
// '{"name":"Alice"}'
JSON.parse() throws a SyntaxError on invalid input. Always wrap it in a try/catch when parsing untrusted data.
function safeParse(str) {
try {
return JSON.parse(str);
} catch (e) {
console.error("Invalid JSON:", e.message);
return null;
}
}
safeParse('{"valid":true}'); // { valid: true }
safeParse("not json at all"); // null (error logged)
fetch("https://api.example.com/users")
.then(response => {
if (!response.ok) throw new Error("HTTP " + response.status);
return response.json(); // parses the JSON body
})
.then(data => console.log(data))
.catch(err => console.error(err));
// async/await version
async function getUsers() {
const res = await fetch("https://api.example.com/users");
if (!res.ok) throw new Error("HTTP " + res.status);
const data = await res.json();
return data;
}
Set the method to POST (or PUT), set the Content-Type header to application/json, and pass the serialized body.
const newUser = { name: "Alice", age: 30 };
fetch("https://api.example.com/users", {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(newUser)
})
.then(res => res.json())
.then(data => console.log("Created:", data));
The correct MIME type is application/json. Always set this header when sending JSON in a request body so the server knows how to parse the payload.
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
JSONP (JSON with Padding) was a workaround for the same-origin policy that blocked cross-domain AJAX requests. It works by injecting a <script> tag pointing to a remote URL. The server wraps its JSON response in a callback function name provided by the client.
// Client requests:
// https://api.example.com/data?callback=handleData
// Server responds with:
handleData({ "key": "value" });
// Client has defined:
function handleData(data) {
console.log(data);
}
Note: JSONP is largely obsolete — modern APIs use CORS headers instead, which is safer.
JSON Schema is a vocabulary that allows you to describe the structure and validation rules for JSON documents. It defines what keys are required, what types values must be, min/max values, pattern constraints, etc.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name", "age"]
}
eval() was used before JSON.parse existed but is extremely dangerous — it executes any JavaScript code, including malicious code injected into the string.
JSON.parse() only parses valid JSON syntax and never executes arbitrary code. Always use JSON.parse().
// ✗ DANGEROUS — never do this
const data = eval('(' + untrustedString + ')');
// ✓ SAFE
const data = JSON.parse(untrustedString);
JSON.stringify() throws a TypeError if the object has circular references (an object that refers back to itself). You need to handle them with a custom replacer or a library like flatted.
const obj = { name: "Alice" };
obj.self = obj; // circular!
// Throws: TypeError: Converting circular structure to JSON
JSON.stringify(obj);
// Custom replacer to skip seen objects
function safeStringify(obj) {
const seen = new WeakSet();
return JSON.stringify(obj, (key, value) => {
if (typeof value === "object" && value !== null) {
if (seen.has(value)) return "[Circular]";
seen.add(value);
}
return value;
});
}
safeStringify(obj);
// '{"name":"Alice","self":"[Circular]"}'
JSON5 is a superset of JSON designed to be easier to write by hand. It relaxes several strict JSON rules:
- Keys do not need to be quoted (unless they have special characters).
- Trailing commas are allowed in objects and arrays.
- Single-quoted strings are allowed.
- Comments (
// single-lineand/* multi-line */) are supported. +Infinity,-Infinity, andNaNare valid values.
JSON5 is not natively supported by browsers — you need the json5 npm package to parse it.
NDJSON is a format where each line of a file is a separate, valid JSON value. It is useful for streaming large datasets or log files because you can process each line independently without loading the entire file into memory.
{"id":1,"name":"Alice"}
{"id":2,"name":"Bob"}
{"id":3,"name":"Carol"}
localStorage only stores strings, so you must serialize with JSON.stringify() before saving and deserialize with JSON.parse() when reading.
const settings = { theme: "dark", fontSize: 16 };
// Save
localStorage.setItem("settings", JSON.stringify(settings));
// Retrieve
const saved = JSON.parse(localStorage.getItem("settings"));
console.log(saved.theme); // "dark"
const fs = require("fs");
// Read — synchronous
const raw = fs.readFileSync("data.json", "utf8");
const data = JSON.parse(raw);
// Write — synchronous
fs.writeFileSync("data.json", JSON.stringify(data, null, 2));
// Read — async/await
const { readFile, writeFile } = require("fs/promises");
async function load() {
const raw = await readFile("data.json", "utf8");
return JSON.parse(raw);
}
// require() directly imports JSON (Node.js built-in)
const config = require("./config.json");
package.json is the manifest file for a Node.js project. It stores project metadata and lists all dependencies, dev dependencies, scripts, and configuration. It is how npm knows what packages to install.
{
"name": "my-app",
"version": "1.0.0",
"scripts": {
"start": "node index.js",
"test": "jest"
},
"dependencies": {
"express": "^4.18.0"
},
"devDependencies": {
"jest": "^29.0.0"
}
}
Modern JavaScript (ES2022+) supports JSON import assertions which let you import a JSON file directly as a module. Node.js also supports this with the --experimental-json-modules flag (stable in Node 22+).
// Traditional — manual parse
import { readFileSync } from "fs";
const config = JSON.parse(readFileSync("config.json", "utf8"));
// Modern import assertion (ESM)
import config from "./config.json" assert { type: "json" };
// Node.js CommonJS — synchronous, cached
const config = require("./config.json");
The require() approach caches the result — subsequent require() calls return the same object. JSON.parse(readFileSync(...)) reads fresh from disk every time.