What is JSON?
JSON (JavaScript Object Notation) is a lightweight data-interchange format that's easy for humans to read and write, and easy for machines to parse and generate. It's the standard format for data exchange in web APIs.
📝 JSON Syntax
// Valid JSON structure
{
"name": "John Doe",
"age": 30,
"email": "john@example.com",
"active": true,
"address": {
"street": "123 Main St",
"city": "New York",
"zipCode": "10001"
},
"hobbies": ["reading", "coding", "gaming"],
"salary": null
}
// JSON Rules:
// ✅ Property names must be double-quoted strings
// ✅ String values must use double quotes
// ✅ Numbers: integers or decimals (no NaN, Infinity)
// ✅ Booleans: true or false (lowercase)
// ✅ Arrays: ordered lists
// ✅ Objects: key-value pairs
// ✅ null: represents absence of value
// ❌ No functions, undefined, Date objects, Symbol
// ❌ No comments allowed
// ❌ No trailing commas
// Invalid JSON examples:
{
name: "John", // ❌ unquoted key
'age': 30, // ❌ single quotes
"hobbies": ['coding'], // ❌ single quotes in array
"date": new Date(), // ❌ Date object
"func": function() {} // ❌ function
}
🔄 JSON.parse()
Basic Parsing
// Parse JSON string to JavaScript object
let jsonString = '{"name":"John","age":30}';
let obj = JSON.parse(jsonString);
console.log(obj.name); // 'John'
console.log(obj.age); // 30
console.log(typeof obj); // 'object'
// Parse arrays
let jsonArray = '[1, 2, 3, 4, 5]';
let arr = JSON.parse(jsonArray);
console.log(arr[0]); // 1
// Parse nested structures
let json = `{
"user": {
"name": "John",
"address": {
"city": "New York"
}
}
}`;
let data = JSON.parse(json);
console.log(data.user.address.city); // 'New York'
// Parse primitive values
JSON.parse('true'); // true
JSON.parse('123'); // 123
JSON.parse('"hello"'); // 'hello'
JSON.parse('null'); // null
Error Handling
// JSON.parse() throws on invalid JSON
try {
let obj = JSON.parse('{ invalid json }');
} catch (error) {
console.error('Parse error:', error.message);
// SyntaxError: Unexpected token i in JSON
}
// Safe parsing function
function safeJSONParse(json, defaultValue = null) {
try {
return JSON.parse(json);
} catch (error) {
console.error('Invalid JSON:', error.message);
return defaultValue;
}
}
let data = safeJSONParse('{ invalid }', {});
// Common parse errors
JSON.parse("{'key': 'value'}"); // ❌ Single quotes
JSON.parse('{key: "value"}'); // ❌ Unquoted key
JSON.parse('{"key": undefined}'); // ❌ undefined
JSON.parse('{"key": "value",}'); // ❌ Trailing comma
JSON.parse('NaN'); // ❌ NaN not valid
JSON.parse('Infinity'); // ❌ Infinity not valid
// Valid alternatives
JSON.parse('{"key": "value"}'); // ✅
JSON.parse('{"key": null}'); // ✅
JSON.parse('{"num": 123}'); // ✅
Reviver Function
// Transform values while parsing
let json = '{"name":"John","age":"30"}';
let obj = JSON.parse(json, (key, value) => {
console.log(`Key: ${key}, Value: ${value}`);
// Convert age string to number
if (key === 'age') {
return parseInt(value);
}
return value;
});
console.log(typeof obj.age); // 'number'
// Parse dates from JSON
let json = '{"created":"2024-01-15T10:30:00.000Z"}';
let obj = JSON.parse(json, (key, value) => {
if (typeof value === 'string' && /^\d{4}-/.test(value)) {
let date = new Date(value);
if (!isNaN(date)) {
return date;
}
}
return value;
});
console.log(obj.created instanceof Date); // true
// Filter out properties
let json = '{"name":"John","password":"secret","age":30}';
let obj = JSON.parse(json, (key, value) => {
if (key === 'password') {
return undefined; // Exclude property
}
return value;
});
console.log(obj); // { name: 'John', age: 30 }
// Convert special values
let json = '{"value":"__undefined__"}';
let obj = JSON.parse(json, (key, value) => {
if (value === '__undefined__') {
return undefined;
}
return value;
});
📤 JSON.stringify()
Basic Stringification
// Convert JavaScript object to JSON string
let obj = {
name: 'John',
age: 30,
active: true
};
let json = JSON.stringify(obj);
console.log(json); // '{"name":"John","age":30,"active":true}'
// Stringify arrays
let arr = [1, 2, 3, 4, 5];
console.log(JSON.stringify(arr)); // '[1,2,3,4,5]'
// Stringify nested structures
let data = {
user: {
name: 'John',
address: {
city: 'New York'
}
}
};
console.log(JSON.stringify(data));
// '{"user":{"name":"John","address":{"city":"New York"}}}'
// Stringify primitive values
JSON.stringify(true); // 'true'
JSON.stringify(123); // '123'
JSON.stringify('hello'); // '"hello"'
JSON.stringify(null); // 'null'
Handling Special Values
// undefined becomes undefined (omitted in objects)
let obj = { a: 1, b: undefined, c: 3 };
JSON.stringify(obj); // '{"a":1,"c":3}'
// undefined in arrays becomes null
let arr = [1, undefined, 3];
JSON.stringify(arr); // '[1,null,3]'
// Functions are omitted
let obj = {
name: 'John',
greet: function() { return 'Hi'; }
};
JSON.stringify(obj); // '{"name":"John"}'
// Symbols are omitted
let obj = {
name: 'John',
[Symbol('id')]: 123
};
JSON.stringify(obj); // '{"name":"John"}'
// Dates become ISO strings
let obj = {
created: new Date('2024-01-15')
};
JSON.stringify(obj); // '{"created":"2024-01-15T00:00:00.000Z"}'
// NaN and Infinity become null
let obj = {
a: NaN,
b: Infinity,
c: -Infinity
};
JSON.stringify(obj); // '{"a":null,"b":null,"c":null}'
// Circular references throw error
let obj = {};
obj.self = obj;
JSON.stringify(obj); // TypeError: Converting circular structure
Replacer Function
// Transform values while stringifying
let user = {
name: 'John',
password: 'secret123',
age: 30
};
let json = JSON.stringify(user, (key, value) => {
// Hide password
if (key === 'password') {
return undefined;
}
return value;
});
console.log(json); // '{"name":"John","age":30}'
// Convert dates to custom format
let obj = {
name: 'John',
created: new Date('2024-01-15')
};
let json = JSON.stringify(obj, (key, value) => {
if (value instanceof Date) {
return value.toLocaleDateString();
}
return value;
});
// Uppercase string values
let obj = { name: 'john', city: 'new york' };
let json = JSON.stringify(obj, (key, value) => {
if (typeof value === 'string') {
return value.toUpperCase();
}
return value;
});
console.log(json); // '{"name":"JOHN","city":"NEW YORK"}'
// Array replacer (whitelist properties)
let user = {
name: 'John',
email: 'john@example.com',
password: 'secret',
age: 30
};
let json = JSON.stringify(user, ['name', 'email']);
console.log(json); // '{"name":"John","email":"john@example.com"}'
Pretty Printing
// Third parameter: space for indentation
let obj = {
name: 'John',
age: 30,
address: {
city: 'New York',
zip: '10001'
}
};
// Number of spaces
let json = JSON.stringify(obj, null, 2);
console.log(json);
/*
{
"name": "John",
"age": 30,
"address": {
"city": "New York",
"zip": "10001"
}
}
*/
// Custom indentation string
let json = JSON.stringify(obj, null, '\t');
// 4 spaces
let json = JSON.stringify(obj, null, 4);
// Max 10 characters for space parameter
let json = JSON.stringify(obj, null, ' ');
🔧 Common Patterns
Deep Clone
// Simple deep clone using JSON
let original = {
name: 'John',
age: 30,
address: {
city: 'New York'
},
hobbies: ['coding', 'reading']
};
let clone = JSON.parse(JSON.stringify(original));
clone.address.city = 'Boston';
console.log(original.address.city); // 'New York' (unchanged)
// Limitations of JSON clone:
// ❌ Loses functions
// ❌ Loses undefined values
// ❌ Loses Symbols
// ❌ Loses Date objects (becomes string)
// ❌ Can't handle circular references
// ❌ Loses Map, Set, RegExp, Error
let obj = {
date: new Date(),
func: () => {},
undef: undefined,
map: new Map([['key', 'value']])
};
let clone = JSON.parse(JSON.stringify(obj));
console.log(clone);
// { date: '2024-01-15T...' } - only date as string
// Better alternatives:
// structuredClone (modern browsers)
let clone = structuredClone(original);
// Or custom deep clone
function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj);
}
if (obj instanceof Array) {
return obj.map(deepClone);
}
let clone = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
clone[key] = deepClone(obj[key]);
}
}
return clone;
}
API Request/Response
// Send JSON to API
async function createUser(userData) {
let response = await fetch('/api/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return response.json();
}
// Usage
try {
let user = await createUser({
name: 'John',
email: 'john@example.com'
});
console.log('Created:', user);
} catch (error) {
console.error('Error:', error);
}
// Parse response safely
async function fetchData(url) {
let response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
let text = await response.text();
try {
return JSON.parse(text);
} catch (error) {
console.error('Invalid JSON response:', text);
throw new Error('Invalid JSON');
}
}
// Handle different response types
async function apiRequest(url) {
let response = await fetch(url);
let contentType = response.headers.get('Content-Type');
if (contentType && contentType.includes('application/json')) {
return response.json();
} else {
return response.text();
}
}
Local Storage
// Store object in localStorage
function saveToStorage(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Storage error:', error);
}
}
function loadFromStorage(key, defaultValue = null) {
try {
let item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.error('Parse error:', error);
return defaultValue;
}
}
// Usage
saveToStorage('user', { name: 'John', age: 30 });
let user = loadFromStorage('user', {});
// Storage wrapper class
class Storage {
static set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (error) {
console.error('Storage error:', error);
return false;
}
}
static get(key, defaultValue = null) {
try {
let item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.error('Parse error:', error);
return defaultValue;
}
}
static remove(key) {
localStorage.removeItem(key);
}
static clear() {
localStorage.clear();
}
}
// Usage
Storage.set('settings', { theme: 'dark', fontSize: 16 });
let settings = Storage.get('settings', {});
Configuration Files
// Load config from JSON file
async function loadConfig() {
try {
let response = await fetch('/config.json');
if (!response.ok) {
throw new Error('Config not found');
}
let config = await response.json();
return config;
} catch (error) {
console.error('Config error:', error);
// Return defaults
return {
apiUrl: 'http://localhost:3000',
timeout: 5000
};
}
}
// Usage
let config = await loadConfig();
console.log(config.apiUrl);
// Validate config structure
function validateConfig(config) {
let required = ['apiUrl', 'timeout'];
for (let key of required) {
if (!(key in config)) {
throw new Error(`Missing config: ${key}`);
}
}
if (typeof config.timeout !== 'number') {
throw new Error('timeout must be number');
}
return config;
}
💡 Practical Examples
Data Transformation
// Transform API response
function transformUser(apiUser) {
return {
id: apiUser.user_id,
name: apiUser.full_name,
email: apiUser.email_address,
created: new Date(apiUser.created_at),
active: apiUser.status === 'active'
};
}
let apiResponse = {
user_id: 123,
full_name: 'John Doe',
email_address: 'john@example.com',
created_at: '2024-01-15T10:30:00Z',
status: 'active'
};
let user = transformUser(apiResponse);
// Batch transformation
function transformUsers(apiUsers) {
return apiUsers.map(transformUser);
}
// Reverse transformation
function toAPIFormat(user) {
return {
user_id: user.id,
full_name: user.name,
email_address: user.email,
created_at: user.created.toISOString(),
status: user.active ? 'active' : 'inactive'
};
}
JSON Schema Validation
// Simple validation
function validateUser(data) {
let errors = [];
if (!data.name || typeof data.name !== 'string') {
errors.push('name is required and must be string');
}
if (!data.email || !/\S+@\S+\.\S+/.test(data.email)) {
errors.push('valid email is required');
}
if (data.age !== undefined) {
if (typeof data.age !== 'number' || data.age < 0) {
errors.push('age must be positive number');
}
}
return {
valid: errors.length === 0,
errors
};
}
// Usage
let result = validateUser({ name: 'John', email: 'invalid' });
if (!result.valid) {
console.error('Validation errors:', result.errors);
}
// Validate with schema
function validate(data, schema) {
let errors = [];
for (let [key, rules] of Object.entries(schema)) {
let value = data[key];
if (rules.required && value === undefined) {
errors.push(`${key} is required`);
continue;
}
if (value !== undefined && rules.type) {
let actualType = Array.isArray(value) ? 'array' : typeof value;
if (actualType !== rules.type) {
errors.push(`${key} must be ${rules.type}`);
}
}
if (rules.min !== undefined && value < rules.min) {
errors.push(`${key} must be >= ${rules.min}`);
}
if (rules.pattern && !rules.pattern.test(value)) {
errors.push(`${key} is invalid format`);
}
}
return { valid: errors.length === 0, errors };
}
// Usage
let schema = {
name: { required: true, type: 'string' },
email: { required: true, pattern: /\S+@\S+\.\S+/ },
age: { type: 'number', min: 0 }
};
let result = validate(data, schema);
Export/Import Data
// Export data as JSON file
function exportData(data, filename) {
let json = JSON.stringify(data, null, 2);
let blob = new Blob([json], { type: 'application/json' });
let url = URL.createObjectURL(blob);
let a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
// Usage
let data = { users: [...], settings: {...} };
exportData(data, 'export.json');
// Import JSON file
function importData(file) {
return new Promise((resolve, reject) => {
let reader = new FileReader();
reader.onload = e => {
try {
let data = JSON.parse(e.target.result);
resolve(data);
} catch (error) {
reject(new Error('Invalid JSON file'));
}
};
reader.onerror = () => {
reject(new Error('File read error'));
};
reader.readAsText(file);
});
}
// Usage
let fileInput = document.querySelector('input[type="file"]');
fileInput.addEventListener('change', async () => {
let file = fileInput.files[0];
try {
let data = await importData(file);
console.log('Imported:', data);
} catch (error) {
console.error('Import error:', error);
}
});
Diff and Merge
// Compare two objects
function diff(obj1, obj2) {
let changes = {};
let allKeys = new Set([
...Object.keys(obj1),
...Object.keys(obj2)
]);
for (let key of allKeys) {
let val1 = obj1[key];
let val2 = obj2[key];
if (JSON.stringify(val1) !== JSON.stringify(val2)) {
changes[key] = { old: val1, new: val2 };
}
}
return changes;
}
let old = { name: 'John', age: 30, city: 'NYC' };
let new = { name: 'John', age: 31, country: 'USA' };
let changes = diff(old, new);
// { age: {old: 30, new: 31}, city: {old: 'NYC', new: undefined}, country: {old: undefined, new: 'USA'} }
// Deep merge objects
function merge(target, source) {
for (let key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
target[key] = merge(target[key] || {}, source[key]);
} else {
target[key] = source[key];
}
}
return target;
}
let defaults = { theme: 'light', fontSize: 16, colors: { bg: '#fff' } };
let user = { fontSize: 18, colors: { text: '#000' } };
let config = merge({...defaults}, user);
// { theme: 'light', fontSize: 18, colors: { bg: '#fff', text: '#000' } }
⚡ Performance Tips
// Parse large JSON incrementally
// For huge files, consider streaming parsers
// Stringify performance
// Avoid stringifying large objects repeatedly
// Cache results when possible
let cache = new Map();
function getCachedJSON(key, obj) {
if (!cache.has(key)) {
cache.set(key, JSON.stringify(obj));
}
return cache.get(key);
}
// Use replacer to limit depth
function limitDepth(obj, maxDepth) {
return JSON.stringify(obj, (key, value) => {
if (typeof value === 'object' && value !== null) {
if (Object.keys(value).length > maxDepth) {
return '[Object]';
}
}
return value;
});
}
// Avoid parsing same data multiple times
let cachedData;
function getData() {
if (!cachedData) {
cachedData = JSON.parse(largeJSONString);
}
return cachedData;
}
🎯 Key Takeaways
- JSON.parse(): Converts JSON string to JavaScript object
- JSON.stringify(): Converts JavaScript object to JSON string
- Reviver/Replacer: Transform values during parse/stringify
- Error Handling: Always wrap parse in try-catch block
- Limitations: Can't serialize functions, undefined, Symbols, circular refs
- Deep Clone: Use JSON for simple objects, structuredClone for complex
- Pretty Print: Third parameter adds indentation for readability
- Type Safety: Validate parsed data, don't trust external JSON