Skip to main content

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 vs JavaScript Objects

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 (true or false)
  • 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
Common Mistakes
  • ❌ 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 Validate

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"
Deep Clone Limitations

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 Obsolete

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, '');
}
Security

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
Next Steps

In Module 12, you'll learn about Error Handling and Debugging to write robust, maintainable code.


Practice Exercises​

  1. Create a function to serialize and deserialize complex objects with dates
  2. Build a localStorage wrapper with JSON encoding/decoding
  3. Implement a JSON validator with custom rules
  4. Create a data transformation pipeline (snake_case ↔ camelCase)
  5. Build an API client with automatic JSON handling
  6. Implement deep cloning that preserves functions and dates
  7. Create a JSON prettifier/minifier tool
  8. Build a JSON diff tool to compare two objects

Additional Resources​