Module 11: JSON (JavaScript Object Notation)
JSON is the universal data format for APIs, configuration files, and data exchange. Understanding JSON is essential for modern web development.
1. What is JSON?
JSON (JavaScript Object Notation) is a lightweight, text-based data format that's easy for humans to read and write, and easy for machines to parse and generate.
1.1 JSON Characteristics
- Text-based format
- Language-independent (despite its name)
- Subset of JavaScript object syntax
- Used for data serialization and transmission
- Human-readable and machine-parseable
JSON is a data format, not a programming language. It looks like JavaScript objects but has stricter syntax rules.
1.2 Use Cases
- API Responses – RESTful APIs return JSON
- Configuration Files – package.json, tsconfig.json
- Data Storage – NoSQL databases (MongoDB)
- Data Exchange – Between frontend and backend
- Local Storage – Storing data in browser
2. JSON Syntax Rules
2.1 Data Types Supported
{
"string": "Hello",
"number": 42,
"boolean": true,
"null": null,
"array": [1, 2, 3],
"object": {
"nested": "value"
}
}
Supported:
- String (double quotes only)
- Number (integer or float)
- Boolean (
trueorfalse) - null
- Array
- Object
NOT Supported:
- Functions
- undefined
- Date objects
- Regular expressions
- Symbols
- Comments
2.2 Syntax Rules
{
"name": "John Doe",
"age": 30,
"isActive": true,
"hobbies": ["reading", "coding"],
"address": {
"city": "New York",
"country": "USA"
}
}
Rules:
- Property names must be in double quotes
- Strings must use double quotes (not single)
- No trailing commas
- No comments allowed
- No functions or methods
- ❌ Single quotes:
{'name': 'John'} - ❌ Unquoted keys:
{name: "John"} - ❌ Trailing commas:
{"name": "John",} - ❌ Comments:
// This will break JSON - ✅ Correct:
{"name": "John"}
3. JSON.stringify()
Converts JavaScript objects to JSON strings.
3.1 Basic Usage
const user = {
name: 'John Doe',
age: 30,
isActive: true
};
const jsonString = JSON.stringify(user);
console.log(jsonString);
// '{"name":"John Doe","age":30,"isActive":true}'
console.log(typeof jsonString); // "string"
3.2 Formatting (Pretty Print)
const user = {
name: 'John Doe',
age: 30,
address: {
city: 'New York',
country: 'USA'
}
};
// With indentation (2 spaces)
const formatted = JSON.stringify(user, null, 2);
console.log(formatted);
/*
{
"name": "John Doe",
"age": 30,
"address": {
"city": "New York",
"country": "USA"
}
}
*/
// With tabs
const tabFormatted = JSON.stringify(user, null, '\t');
3.3 Replacer Function
const user = {
name: 'John Doe',
age: 30,
password: 'secret123',
email: 'john@example.com'
};
// Filter out sensitive data
const json = JSON.stringify(user, (key, value) => {
if (key === 'password') {
return undefined; // Exclude from output
}
return value;
});
console.log(json);
// '{"name":"John Doe","age":30,"email":"john@example.com"}'
3.4 Replacer Array
const user = {
name: 'John Doe',
age: 30,
password: 'secret123',
email: 'john@example.com'
};
// Only include specific properties
const json = JSON.stringify(user, ['name', 'email']);
console.log(json);
// '{"name":"John Doe","email":"john@example.com"}'
3.5 toJSON Method
class User {
constructor(name, age, password) {
this.name = name;
this.age = age;
this.password = password;
}
toJSON() {
// Custom serialization
return {
name: this.name,
age: this.age
// password excluded
};
}
}
const user = new User('John', 30, 'secret123');
console.log(JSON.stringify(user));
// '{"name":"John","age":30}'
3.6 Handling Special Values
const data = {
name: 'John',
age: undefined, // Will be omitted
active: null, // Will be included
greet: function() {} // Will be omitted
};
console.log(JSON.stringify(data));
// '{"name":"John","active":null}'
// In arrays, undefined becomes null
const arr = [1, undefined, 3, null];
console.log(JSON.stringify(arr));
// '[1,null,3,null]'
4. JSON.parse()
Converts JSON strings to JavaScript objects.
4.1 Basic Usage
const jsonString = '{"name":"John Doe","age":30,"isActive":true}';
const user = JSON.parse(jsonString);
console.log(user.name); // "John Doe"
console.log(user.age); // 30
console.log(typeof user); // "object"
4.2 Reviver Function
const jsonString = '{"name":"John","createdAt":"2024-03-15T10:30:00.000Z"}';
const user = JSON.parse(jsonString, (key, value) => {
// Convert date strings to Date objects
if (key === 'createdAt') {
return new Date(value);
}
return value;
});
console.log(user.createdAt instanceof Date); // true
console.log(user.createdAt.getFullYear()); // 2024
4.3 Error Handling
const invalidJSON = '{"name": "John",}'; // Trailing comma
try {
const data = JSON.parse(invalidJSON);
} catch (error) {
console.error('Invalid JSON:', error.message);
// Invalid JSON: Unexpected token } in JSON at position 18
}
Always wrap JSON.parse() in try-catch when parsing untrusted data.
4.4 Parsing Arrays
const jsonArray = '[1, 2, 3, 4, 5]';
const numbers = JSON.parse(jsonArray);
console.log(Array.isArray(numbers)); // true
console.log(numbers); // [1, 2, 3, 4, 5]
5. Common Patterns
5.1 Deep Cloning Objects
const original = {
name: 'John',
address: {
city: 'New York'
}
};
// Deep clone using JSON
const clone = JSON.parse(JSON.stringify(original));
clone.address.city = 'Los Angeles';
console.log(original.address.city); // "New York" (unchanged)
console.log(clone.address.city); // "Los Angeles"
JSON clone doesn't preserve:
- Functions
- undefined values
- Symbols
- Date objects (converted to strings)
- Regular expressions
- Circular references
5.2 API Communication
// Sending data to API
async function createUser(userData) {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
const data = await response.json();
return data;
}
// Usage
const newUser = {
name: 'John Doe',
email: 'john@example.com'
};
createUser(newUser).then(user => {
console.log('Created:', user);
});
5.3 LocalStorage Integration
// Save to localStorage
function saveUser(user) {
localStorage.setItem('user', JSON.stringify(user));
}
// Load from localStorage
function loadUser() {
const json = localStorage.getItem('user');
return json ? JSON.parse(json) : null;
}
// Usage
const user = { name: 'John', age: 30 };
saveUser(user);
const loadedUser = loadUser();
console.log(loadedUser); // { name: 'John', age: 30 }
5.4 Configuration Files
// Read config.json
async function loadConfig() {
const response = await fetch('/config.json');
const config = await response.json();
return config;
}
// Usage
loadConfig().then(config => {
console.log('API URL:', config.apiUrl);
console.log('Debug mode:', config.debug);
});
6. Working with Complex Data
6.1 Nested Objects
const company = {
name: 'Tech Corp',
employees: [
{
id: 1,
name: 'John Doe',
department: {
name: 'Engineering',
location: 'Building A'
}
},
{
id: 2,
name: 'Jane Smith',
department: {
name: 'Marketing',
location: 'Building B'
}
}
]
};
const json = JSON.stringify(company, null, 2);
console.log(json);
const parsed = JSON.parse(json);
console.log(parsed.employees[0].department.name); // "Engineering"
6.2 Handling Dates
const event = {
title: 'Meeting',
date: new Date('2024-03-15T10:30:00')
};
// Dates become strings
const json = JSON.stringify(event);
console.log(json);
// '{"title":"Meeting","date":"2024-03-15T10:30:00.000Z"}'
// Parse back with date conversion
const parsed = JSON.parse(json, (key, value) => {
if (key === 'date') {
return new Date(value);
}
return value;
});
console.log(parsed.date instanceof Date); // true
6.3 Custom Serialization
class Product {
constructor(id, name, price) {
this.id = id;
this.name = name;
this.price = price;
this.createdAt = new Date();
}
toJSON() {
return {
id: this.id,
name: this.name,
price: this.price,
createdAt: this.createdAt.toISOString()
};
}
static fromJSON(json) {
const obj = JSON.parse(json);
const product = new Product(obj.id, obj.name, obj.price);
product.createdAt = new Date(obj.createdAt);
return product;
}
}
// Serialize
const product = new Product(1, 'Laptop', 999);
const json = JSON.stringify(product);
console.log(json);
// Deserialize
const restored = Product.fromJSON(json);
console.log(restored instanceof Product); // true
console.log(restored.createdAt instanceof Date); // true
7. JSON Validation
7.1 Checking Valid JSON
function isValidJSON(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
console.log(isValidJSON('{"name":"John"}')); // true
console.log(isValidJSON('{name:"John"}')); // false
console.log(isValidJSON('{"name":"John",}')); // false
7.2 Schema Validation
function validateUser(user) {
const errors = [];
if (!user.name || typeof user.name !== 'string') {
errors.push('Name must be a string');
}
if (!user.age || typeof user.age !== 'number') {
errors.push('Age must be a number');
}
if (!user.email || !user.email.includes('@')) {
errors.push('Valid email required');
}
return {
valid: errors.length === 0,
errors
};
}
const userData = '{"name":"John","age":30,"email":"john@example.com"}';
const user = JSON.parse(userData);
const validation = validateUser(user);
if (!validation.valid) {
console.error('Validation errors:', validation.errors);
}
8. Performance Considerations
8.1 Large JSON Files
// Streaming large JSON (Node.js)
const fs = require('fs');
const JSONStream = require('JSONStream');
fs.createReadStream('large-file.json')
.pipe(JSONStream.parse('items.*'))
.on('data', (item) => {
console.log('Processing item:', item);
});
8.2 Compression
// Using compression for API responses
async function fetchCompressedData() {
const response = await fetch('https://api.example.com/data', {
headers: {
'Accept-Encoding': 'gzip, deflate'
}
});
const data = await response.json();
return data;
}
8.3 Benchmarking
const obj = { name: 'John', age: 30, items: Array(1000).fill({ id: 1 }) };
// Measure stringify performance
console.time('stringify');
const json = JSON.stringify(obj);
console.timeEnd('stringify');
// Measure parse performance
console.time('parse');
const parsed = JSON.parse(json);
console.timeEnd('parse');
9. JSON in Different Contexts
9.1 Fetch API
// GET request
async function getUsers() {
const response = await fetch('https://api.example.com/users');
const users = await response.json(); // Parse JSON automatically
return users;
}
// POST request
async function createUser(user) {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(user)
});
return await response.json();
}
9.2 WebSockets
const ws = new WebSocket('ws://localhost:8080');
// Send JSON
ws.send(JSON.stringify({
type: 'message',
content: 'Hello'
}));
// Receive JSON
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Received:', data);
};
9.3 Service Workers
// Cache JSON responses
self.addEventListener('fetch', (event) => {
if (event.request.url.includes('/api/')) {
event.respondWith(
caches.match(event.request).then(cached => {
if (cached) {
return cached;
}
return fetch(event.request).then(response => {
return caches.open('api-cache').then(cache => {
cache.put(event.request, response.clone());
return response;
});
});
})
);
}
});
10. JSON Alternatives
10.1 JSONP (Legacy)
// JSONP for cross-origin requests (before CORS)
function loadJSONP(url, callback) {
const script = document.createElement('script');
script.src = `${url}?callback=${callback}`;
document.body.appendChild(script);
}
// Callback function
window.handleData = function(data) {
console.log('Received:', data);
};
loadJSONP('https://api.example.com/data', 'handleData');
JSONP is deprecated. Use CORS-enabled fetch instead.
10.2 JSON5 (Extended JSON)
// JSON5 allows:
// - Comments
// - Trailing commas
// - Unquoted keys
// - Single quotes
const JSON5 = require('json5');
const data = JSON5.parse(`{
// This is a comment
name: 'John', // Unquoted key
hobbies: ['reading', 'coding',], // Trailing comma
}`);
console.log(data);
10.3 Other Formats
// YAML (more human-readable)
// name: John Doe
// age: 30
// hobbies:
// - reading
// - coding
// BSON (Binary JSON - MongoDB)
// More compact, supports additional types
// MessagePack (Binary serialization)
// Smaller and faster than JSON
11. Best Practices
11.1 Error Handling
function safeJSONParse(str, fallback = null) {
try {
return JSON.parse(str);
} catch (e) {
console.error('JSON parse error:', e.message);
return fallback;
}
}
const data = safeJSONParse(userInput, {});
11.2 Type Safety
function parseUserJSON(json) {
const data = JSON.parse(json);
// Validate expected structure
if (!data || typeof data !== 'object') {
throw new Error('Invalid user data');
}
return {
name: String(data.name || ''),
age: Number(data.age || 0),
email: String(data.email || '')
};
}
11.3 Security
// Never use eval() to parse JSON
const jsonString = '{"name":"John"}';
// ❌ DANGEROUS - Never do this
const data = eval('(' + jsonString + ')');
// ✅ SAFE - Use JSON.parse
const data = JSON.parse(jsonString);
// Sanitize user input
function sanitizeJSON(str) {
// Remove potentially dangerous content
return str.replace(/<script>/gi, '');
}
Never use eval() to parse JSON. Always use JSON.parse(). Validate and sanitize data from untrusted sources.
11.4 Naming Conventions
// Use camelCase for JavaScript
const userData = {
firstName: 'John',
lastName: 'Doe',
emailAddress: 'john@example.com'
};
// API might use snake_case
const apiData = {
first_name: 'John',
last_name: 'Doe',
email_address: 'john@example.com'
};
// Conversion helper
function toCamelCase(obj) {
const result = {};
for (const key in obj) {
const camelKey = key.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
result[camelKey] = obj[key];
}
return result;
}
Summary
In this module, you learned:
- ✅ What JSON is and its syntax rules
- ✅ Converting between JavaScript objects and JSON with stringify/parse
- ✅ Formatting, filtering, and transforming JSON data
- ✅ Handling dates, nested objects, and special values
- ✅ Common patterns: API communication, localStorage, cloning
- ✅ JSON validation and error handling
- ✅ Performance considerations and best practices
- ✅ Security concerns and alternatives
In Module 12, you'll learn about Error Handling and Debugging to write robust, maintainable code.
Practice Exercises
- Create a function to serialize and deserialize complex objects with dates
- Build a localStorage wrapper with JSON encoding/decoding
- Implement a JSON validator with custom rules
- Create a data transformation pipeline (snake_case ↔ camelCase)
- Build an API client with automatic JSON handling
- Implement deep cloning that preserves functions and dates
- Create a JSON prettifier/minifier tool
- Build a JSON diff tool to compare two objects