Working with JSON in JavaScript: Complete Guide for Developers
You’ve called an API. You got back data. You tried to access a property. And suddenly:
TypeError: Cannot read property 'name' of undefined
Or worse:
SyntaxError: Unexpected token o in JSON at position 1
Your JSON looks fine in the browser. The API documentation says it returns JSON. But when you try to use it in JavaScript, everything breaks.
Welcome to the JSON in JavaScript maze.
The problem isn’t that JSON is complex—it’s that the relationship between JSON and JavaScript is confusing. They look identical but behave differently. JSON is a string format. JavaScript objects are data structures. Converting between them seems simple until you hit circular references, lose date precision, or encounter undefined values.
Most tutorials show you JSON.parse() and JSON.stringify() and call it a day. But real applications need error handling, type validation, performance optimization, and handling edge cases like large datasets, circular structures, and API inconsistencies.
This guide is different. You’ll learn not just how to parse and stringify, but how to handle errors gracefully, work with APIs effectively, optimize performance, handle complex data structures, and debug common JSON issues.
By the end, you’ll understand exactly when to use JSON vs JavaScript objects, how to handle every data type correctly, how to validate JSON before parsing, and how to build robust data pipelines that handle anything APIs throw at you.
Quick Answer: JSON in JavaScript Essentials
Don’t have time for 6,000 words? Here’s what you need to know:
- What JSON is: A text format for data exchange, not the same as JavaScript objects
- Two key methods:
JSON.parse()(string → object) andJSON.stringify()(object → string) - Most common use: Receiving data from APIs and sending data to servers
- Key difference: JSON uses double quotes, no functions, no undefined, strict syntax
- Common error: Trying to parse already-parsed objects or HTML responses
- Quick tools: Use our JSON Formatter to validate and format JSON instantly
- Best practice: Always use try/catch when parsing JSON from external sources
Still here? Let’s master JSON in JavaScript.
What is JSON in JavaScript?
JSON (JavaScript Object Notation) is a text-based data format that was inspired by JavaScript object syntax but is not the same as JavaScript objects.
JSON vs JavaScript Objects: Key Differences
This is the most important concept to understand. They look similar but are fundamentally different.
JSON (a string):
const jsonString = '{"name":"John","age":30,"active":true}';
typeof jsonString; // "string"
JavaScript Object:
const jsObject = {name: "John", age: 30, active: true};
typeof jsObject; // "object"
Visual Comparison:
| Feature | JavaScript Object | JSON |
|---|---|---|
| Format | Data structure in memory | Text string |
| Keys | Can be unquoted | Must be double-quoted |
| Strings | Single or double quotes | Only double quotes |
| Values | Any JavaScript value | String, number, boolean, array, object, null |
| Functions | Allowed | Not allowed |
| Undefined | Allowed | Not allowed |
| Comments | Allowed | Not allowed |
| Trailing commas | Allowed (ES5+) | Not allowed |
| Methods | Can have methods | No methods |
Example of all differences:
// ✅ Valid JavaScript Object
const jsObj = {
name: 'John', // Single quotes OK
age: 30, // No quotes on numbers
hobbies: ['reading',], // Trailing comma OK
greet: function() { // Functions allowed
return 'Hello';
},
middle: undefined, // undefined allowed
// This is a comment
};
// ❌ This is NOT valid JSON
const notJson = '{"name": "John", "greet": function() {}}';
// ✅ Valid JSON (as a string)
const validJson = '{"name":"John","age":30,"hobbies":["reading"]}';
Key insight: JSON is always a string until you parse it. Even though it looks like an object, it’s text.
Why JSON is Essential for Web Development
1. Universal data exchange format
// Server sends this:
'{"userId": 123, "username": "john"}'
// JavaScript receives and parses:
const user = JSON.parse(response);
console.log(user.username); // "john"
2. Language-independent
- Python can read JSON
- Java can read JSON
- PHP can read JSON
- Go can read JSON
Everyone speaks JSON.
3. Human-readable
{
"product": "Laptop",
"price": 999.99,
"inStock": true
}
You can read and understand it without a decoder.
4. Lightweight
- Smaller than XML
- Faster to parse than XML
- Native browser support
JSON’s Role in APIs and Data Exchange
Every modern API uses JSON:
// Making an API request
fetch('https://api.example.com/users/123')
.then(response => response.json()) // Parses JSON automatically
.then(data => {
console.log(data.name); // Access as JavaScript object
});
Common JSON scenarios:
- REST APIs - Sending and receiving data
- Configuration files - package.json, config.json
- LocalStorage - Storing complex data in the browser
- WebSockets - Real-time data exchange
- NoSQL databases - MongoDB stores JSON-like documents
Browser and Node.js Support
All modern environments support JSON natively:
Browser:
- Supported since IE8+ (with limitations)
- Modern browsers: Full support
- Available globally as
JSONobject
Node.js:
- Built-in support (all versions)
- Same
JSONAPI as browsers - Additional file system methods
No libraries needed! JSON.parse() and JSON.stringify() are built into JavaScript.
Tools: Validate and format JSON with our JSON Formatter and compare JSON structures with our Diff Tool.
JSON.parse(): Converting Strings to Objects
JSON.parse() transforms a JSON string into a JavaScript object you can work with.
Basic Syntax and Usage
Syntax:
JSON.parse(text, reviver)
Basic example:
const jsonString = '{"name":"John","age":30}';
const obj = JSON.parse(jsonString);
console.log(obj.name); // "John"
console.log(obj.age); // 30
console.log(typeof obj); // "object"
Parsing different types:
// Object
JSON.parse('{"key":"value"}'); // {key: "value"}
// Array
JSON.parse('[1,2,3]'); // [1, 2, 3]
// String
JSON.parse('"hello"'); // "hello"
// Number
JSON.parse('123'); // 123
// Boolean
JSON.parse('true'); // true
// Null
JSON.parse('null'); // null
Parsing API Responses
Modern fetch API:
// The .json() method automatically parses JSON
fetch('https://api.example.com/users')
.then(response => response.json()) // Parses JSON automatically
.then(users => {
console.log(users[0].name);
});
Manual parsing:
fetch('https://api.example.com/users')
.then(response => response.text()) // Get as text first
.then(text => {
const users = JSON.parse(text); // Manual parsing
console.log(users);
});
Async/await version:
async function getUsers() {
const response = await fetch('https://api.example.com/users');
const users = await response.json();
return users;
}
XMLHttpRequest (legacy):
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.example.com/users');
xhr.onload = function() {
if (xhr.status === 200) {
const users = JSON.parse(xhr.responseText);
console.log(users);
}
};
xhr.send();
Error Handling (Try/Catch for Invalid JSON)
Always wrap JSON.parse() in try/catch when working with external data:
function safeJsonParse(jsonString) {
try {
return JSON.parse(jsonString);
} catch (error) {
console.error('Failed to parse JSON:', error.message);
return null;
}
}
const data = safeJsonParse(apiResponse);
if (data) {
console.log('Success:', data);
} else {
console.log('Invalid JSON received');
}
Better error handling with context:
function parseWithContext(jsonString, source = 'unknown') {
try {
return {
success: true,
data: JSON.parse(jsonString)
};
} catch (error) {
console.error(`JSON parse error from ${source}:`, error.message);
// Extract position from error message
const match = error.message.match(/position (\d+)/);
const position = match ? parseInt(match[1]) : 0;
return {
success: false,
error: error.message,
position: position,
preview: jsonString.substring(Math.max(0, position - 20), position + 20)
};
}
}
const result = parseWithContext(apiResponse, 'User API');
if (result.success) {
console.log(result.data);
} else {
console.error('Parse failed at position', result.position);
console.error('Context:', result.preview);
}
Validating before parsing:
function isValidJson(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
if (isValidJson(apiResponse)) {
const data = JSON.parse(apiResponse);
} else {
console.error('Invalid JSON received');
}
The Reviver Function (Second Parameter)
The reviver function lets you transform values during parsing.
Syntax:
JSON.parse(text, (key, value) => {
// Transform value here
return value;
});
Example: Convert date strings to Date objects
const jsonString = '{"name":"John","createdAt":"2025-01-15T10:30:00Z"}';
const data = JSON.parse(jsonString, (key, value) => {
// Check if value looks like a date
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value);
}
return value;
});
console.log(data.createdAt instanceof Date); // true
console.log(data.createdAt.getFullYear()); // 2025
Example: Convert string numbers to actual numbers
const jsonString = '{"price":"19.99","quantity":"5"}';
const data = JSON.parse(jsonString, (key, value) => {
if (key === 'price' || key === 'quantity') {
return parseFloat(value);
}
return value;
});
console.log(typeof data.price); // "number"
console.log(data.price + data.quantity); // 24.99 (not "19.995")
Example: Filter sensitive data
const jsonString = '{"name":"John","password":"secret123","email":"john@example.com"}';
const data = JSON.parse(jsonString, (key, value) => {
if (key === 'password') {
return '[REDACTED]';
}
return value;
});
console.log(data);
// {name: "John", password: "[REDACTED]", email: "john@example.com"}
Real-World Examples
Example 1: Parsing nested API response
const apiResponse = `{
"success": true,
"data": {
"user": {
"id": 123,
"name": "John Doe",
"email": "john@example.com"
},
"posts": [
{"id": 1, "title": "First Post"},
{"id": 2, "title": "Second Post"}
]
}
}`;
try {
const response = JSON.parse(apiResponse);
if (response.success) {
const user = response.data.user;
const posts = response.data.posts;
console.log(`User: ${user.name}`);
posts.forEach(post => {
console.log(`- ${post.title}`);
});
}
} catch (error) {
console.error('Failed to parse API response:', error);
}
Example 2: Reading configuration file (Node.js)
const fs = require('fs');
try {
const configFile = fs.readFileSync('config.json', 'utf8');
const config = JSON.parse(configFile);
console.log('Database:', config.database.host);
console.log('Port:', config.server.port);
} catch (error) {
if (error.code === 'ENOENT') {
console.error('Config file not found');
} else if (error instanceof SyntaxError) {
console.error('Invalid JSON in config file');
} else {
console.error('Error reading config:', error);
}
}
Example 3: LocalStorage retrieval
function getFromStorage(key) {
try {
const item = localStorage.getItem(key);
if (item === null) {
return null;
}
return JSON.parse(item);
} catch (error) {
console.error(`Failed to parse localStorage item "${key}":`, error);
localStorage.removeItem(key); // Remove corrupted data
return null;
}
}
const userData = getFromStorage('user');
if (userData) {
console.log('Welcome back,', userData.name);
}
Tools: Test JSON parsing with our JSON Formatter and validate structure with JSON Schema validation.
JSON.stringify(): Converting Objects to Strings
JSON.stringify() converts JavaScript values into JSON strings for storage or transmission.
Basic Syntax and Usage
Syntax:
JSON.stringify(value, replacer, space)
Basic example:
const user = {
name: "John",
age: 30,
active: true
};
const jsonString = JSON.stringify(user);
console.log(jsonString);
// {"name":"John","age":30,"active":true}
console.log(typeof jsonString); // "string"
Stringifying different types:
// Object
JSON.stringify({name: "John"}); // '{"name":"John"}'
// Array
JSON.stringify([1, 2, 3]); // '[1,2,3]'
// String
JSON.stringify("hello"); // '"hello"'
// Number
JSON.stringify(123); // '123'
// Boolean
JSON.stringify(true); // 'true'
// Null
JSON.stringify(null); // 'null'
// Undefined
JSON.stringify(undefined); // undefined (not a string!)
Important behavior with special values:
// undefined is omitted from objects
JSON.stringify({a: 1, b: undefined, c: 3});
// '{"a":1,"c":3}' (b is missing!)
// undefined becomes null in arrays
JSON.stringify([1, undefined, 3]);
// '[1,null,3]'
// Functions are omitted
JSON.stringify({name: "John", greet: function() {}});
// '{"name":"John"}'
// Symbols are omitted
JSON.stringify({name: "John", id: Symbol('id')});
// '{"name":"John"}'
Formatting Output (Indent Parameter)
Pretty-print JSON with indentation:
const data = {name: "John", age: 30, hobbies: ["reading", "gaming"]};
// No formatting (compact)
JSON.stringify(data);
// '{"name":"John","age":30,"hobbies":["reading","gaming"]}'
// With 2-space indentation
JSON.stringify(data, null, 2);
/*
{
"name": "John",
"age": 30,
"hobbies": [
"reading",
"gaming"
]
}
*/
// With 4-space indentation
JSON.stringify(data, null, 4);
// With tabs
JSON.stringify(data, null, '\t');
When to use formatting:
- Development/debugging: Use indentation for readability
- Production/APIs: Omit indentation to reduce size
- Log files: Use indentation for easier reading
- Configuration files: Use indentation for human editing
// Save to file with formatting
const fs = require('fs');
fs.writeFileSync('config.json', JSON.stringify(config, null, 2));
// Send to API without formatting (smaller payload)
fetch('/api/data', {
method: 'POST',
body: JSON.stringify(data) // Compact
});
The Replacer Function (Filtering Properties)
The replacer lets you control what gets stringified.
Filter specific keys:
const user = {
name: "John",
email: "john@example.com",
password: "secret123",
role: "admin"
};
// Only include specific keys
const safeJson = JSON.stringify(user, ['name', 'email', 'role']);
// '{"name":"John","email":"john@example.com","role":"admin"}'
Use a function for complex filtering:
const user = {
name: "John",
email: "john@example.com",
password: "secret123",
ssn: "123-45-6789"
};
const safeJson = JSON.stringify(user, (key, value) => {
// Hide sensitive fields
if (key === 'password' || key === 'ssn') {
return '[REDACTED]';
}
return value;
});
console.log(safeJson);
// {"name":"John","email":"john@example.com","password":"[REDACTED]","ssn":"[REDACTED]"}
Transform values during stringification:
const data = {
name: "John",
createdAt: new Date('2025-01-15'),
price: 19.99
};
const json = JSON.stringify(data, (key, value) => {
// Convert Date to ISO string
if (value instanceof Date) {
return value.toISOString();
}
// Round prices to 2 decimals
if (key === 'price') {
return Math.round(value * 100) / 100;
}
return value;
});
Remove empty values:
const data = {
name: "John",
email: "",
phone: null,
address: undefined
};
const cleanJson = JSON.stringify(data, (key, value) => {
// Remove null, undefined, and empty strings
if (value === null || value === undefined || value === '') {
return undefined; // Returning undefined omits the property
}
return value;
});
console.log(cleanJson);
// '{"name":"John"}'
Handling Circular References
The problem:
const obj = {name: "John"};
obj.self = obj; // Circular reference
JSON.stringify(obj);
// ❌ TypeError: Converting circular structure to JSON
Solution 1: Remove circular references
function stringifyWithoutCircular(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;
});
}
const obj = {name: "John"};
obj.self = obj;
const json = stringifyWithoutCircular(obj);
console.log(json);
// '{"name":"John","self":"[Circular]"}'
Solution 2: Use flatted library
// npm install flatted
const { stringify, parse } = require('flatted');
const obj = {name: "John"};
obj.self = obj;
const json = stringify(obj); // Handles circular references
const restored = parse(json); // Restores structure
Solution 3: Remove circular properties before stringifying
function removeCircular(obj, seen = new WeakSet()) {
if (typeof obj !== 'object' || obj === null) {
return obj;
}
if (seen.has(obj)) {
return undefined;
}
seen.add(obj);
const result = Array.isArray(obj) ? [] : {};
for (const key in obj) {
result[key] = removeCircular(obj[key], seen);
}
return result;
}
const obj = {name: "John"};
obj.self = obj;
const clean = removeCircular(obj);
const json = JSON.stringify(clean);
Real-World Examples
Example 1: 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)
});
return response.json();
}
await createUser({
name: "John Doe",
email: "john@example.com",
age: 30
});
Example 2: Saving to localStorage
function saveToStorage(key, value) {
try {
const json = JSON.stringify(value);
localStorage.setItem(key, json);
return true;
} catch (error) {
console.error('Failed to save to localStorage:', error);
return false;
}
}
const userData = {
id: 123,
name: "John",
preferences: {
theme: "dark",
notifications: true
}
};
saveToStorage('user', userData);
Example 3: Writing to file (Node.js)
const fs = require('fs');
function saveConfig(config, filename = 'config.json') {
try {
const json = JSON.stringify(config, null, 2);
fs.writeFileSync(filename, json, 'utf8');
console.log(`Config saved to ${filename}`);
} catch (error) {
console.error('Failed to save config:', error);
}
}
const config = {
database: {
host: "localhost",
port: 5432
},
server: {
port: 3000
}
};
saveConfig(config);
Example 4: Deep cloning objects
function deepClone(obj) {
try {
return JSON.parse(JSON.stringify(obj));
} catch (error) {
console.error('Failed to clone object:', error);
return null;
}
}
const original = {
name: "John",
hobbies: ["reading", "gaming"]
};
const clone = deepClone(original);
clone.hobbies.push("coding");
console.log(original.hobbies); // ["reading", "gaming"]
console.log(clone.hobbies); // ["reading", "gaming", "coding"]
⚠️ Warning: Deep cloning with JSON has limitations:
- Functions are removed
- undefined values are removed
- Dates become strings
- Regular expressions become empty objects
- Circular references cause errors
Tools: Format JSON output with our JSON Formatter and generate test UUIDs with our UUID Generator.
Working with JSON Data
Once you’ve parsed JSON, here’s how to manipulate it effectively.
Accessing Nested Properties
Dot notation:
const data = {
user: {
name: "John",
address: {
city: "New York",
zipCode: "10001"
}
}
};
console.log(data.user.name); // "John"
console.log(data.user.address.city); // "New York"
Bracket notation:
console.log(data['user']['name']); // "John"
console.log(data['user']['address']['city']); // "New York"
// Useful for dynamic keys
const key = 'name';
console.log(data.user[key]); // "John"
Safe access with optional chaining (ES2020+):
const data = {
user: {
name: "John"
}
};
// Without optional chaining
console.log(data.user.address.city);
// ❌ TypeError: Cannot read property 'city' of undefined
// With optional chaining
console.log(data.user?.address?.city); // undefined (no error!)
Providing defaults:
const city = data.user?.address?.city || 'Unknown';
console.log(city); // "Unknown"
// Nullish coalescing (ES2020)
const zipCode = data.user?.address?.zipCode ?? '00000';
Deep property access helper:
function getNestedProperty(obj, path, defaultValue = undefined) {
const keys = path.split('.');
let result = obj;
for (const key of keys) {
if (result === null || result === undefined) {
return defaultValue;
}
result = result[key];
}
return result !== undefined ? result : defaultValue;
}
const city = getNestedProperty(data, 'user.address.city', 'Unknown');
const country = getNestedProperty(data, 'user.address.country', 'USA');
Iterating Over JSON Arrays
forEach loop:
const users = [
{id: 1, name: "John"},
{id: 2, name: "Jane"},
{id: 3, name: "Bob"}
];
users.forEach(user => {
console.log(`${user.id}: ${user.name}`);
});
map (transform array):
const names = users.map(user => user.name);
console.log(names); // ["John", "Jane", "Bob"]
const formatted = users.map(user => ({
...user,
displayName: `User: ${user.name}`
}));
filter (find matching items):
const filtered = users.filter(user => user.id > 1);
console.log(filtered); // [{id: 2, name: "Jane"}, {id: 3, name: "Bob"}]
find (get first match):
const user = users.find(u => u.name === "Jane");
console.log(user); // {id: 2, name: "Jane"}
reduce (aggregate data):
const products = [
{name: "Laptop", price: 999},
{name: "Mouse", price: 29},
{name: "Keyboard", price: 79}
];
const total = products.reduce((sum, product) => sum + product.price, 0);
console.log(total); // 1107
Modifying JSON Objects
Add properties:
const user = {name: "John"};
user.age = 30;
user['email'] = 'john@example.com';
console.log(user);
// {name: "John", age: 30, email: "john@example.com"}
Update properties:
user.name = "John Doe";
user.age += 1;
Delete properties:
delete user.email;
console.log(user);
// {name: "John Doe", age: 31}
Merge objects:
const user = {name: "John", age: 30};
const extra = {email: "john@example.com", age: 31};
// Spread operator (ES6+)
const merged = {...user, ...extra};
console.log(merged);
// {name: "John", age: 31, email: "john@example.com"}
// Object.assign()
const merged2 = Object.assign({}, user, extra);
Update nested properties:
const data = {
user: {
name: "John",
settings: {
theme: "light"
}
}
};
// Update nested value
data.user.settings.theme = "dark";
// Add nested property
data.user.settings.notifications = true;
Deep Cloning Objects with JSON
Simple deep clone:
const original = {
name: "John",
hobbies: ["reading", "gaming"],
address: {
city: "New York"
}
};
const clone = JSON.parse(JSON.stringify(original));
// Modify clone
clone.hobbies.push("coding");
clone.address.city = "Boston";
// Original unchanged
console.log(original.hobbies); // ["reading", "gaming"]
console.log(original.address.city); // "New York"
Limitations of JSON cloning:
const obj = {
name: "John",
greet: function() { return "Hello"; }, // Lost!
birthday: new Date('1990-01-15'), // Becomes string!
regex: /test/i, // Becomes {}!
undefined: undefined, // Lost!
symbol: Symbol('id'), // Lost!
infinity: Infinity // Becomes null!
};
const clone = JSON.parse(JSON.stringify(obj));
console.log(typeof clone.greet); // "undefined"
console.log(clone.birthday instanceof Date); // false (it's a string)
console.log(clone.regex); // {}
Better deep clone with structuredClone (modern browsers):
const original = {
name: "John",
birthday: new Date('1990-01-15'),
regex: /test/i
};
const clone = structuredClone(original);
console.log(clone.birthday instanceof Date); // true
console.log(clone.regex instanceof RegExp); // true
Merging JSON Objects
Shallow merge:
const obj1 = {a: 1, b: 2};
const obj2 = {b: 3, c: 4};
const merged = {...obj1, ...obj2};
console.log(merged); // {a: 1, b: 3, c: 4}
Deep merge helper:
function deepMerge(target, source) {
const result = {...target};
for (const key in source) {
if (source[key] instanceof Object && key in target) {
result[key] = deepMerge(target[key], source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
const obj1 = {
user: {
name: "John",
settings: {
theme: "light",
notifications: true
}
}
};
const obj2 = {
user: {
email: "john@example.com",
settings: {
theme: "dark"
}
}
};
const merged = deepMerge(obj1, obj2);
console.log(merged);
/*
{
user: {
name: "John",
email: "john@example.com",
settings: {
theme: "dark",
notifications: true
}
}
}
*/
Tools: Compare JSON objects with our Diff Tool and convert between data formats with our Converter.
Common JSON Operations
Essential patterns for working with JSON in real applications.
Reading JSON from Files (Node.js)
Synchronous (blocking):
const fs = require('fs');
try {
const jsonString = fs.readFileSync('data.json', 'utf8');
const data = JSON.parse(jsonString);
console.log(data);
} catch (error) {
console.error('Error reading file:', error);
}
Asynchronous (non-blocking):
const fs = require('fs').promises;
async function readJsonFile(filename) {
try {
const jsonString = await fs.readFile(filename, 'utf8');
return JSON.parse(jsonString);
} catch (error) {
console.error('Error reading file:', error);
return null;
}
}
const data = await readJsonFile('data.json');
With error handling:
async function loadConfig(filename) {
try {
const content = await fs.readFile(filename, 'utf8');
const config = JSON.parse(content);
return {success: true, data: config};
} catch (error) {
if (error.code === 'ENOENT') {
return {success: false, error: 'File not found'};
} else if (error instanceof SyntaxError) {
return {success: false, error: 'Invalid JSON'};
}
return {success: false, error: error.message};
}
}
Fetching JSON from APIs (fetch, axios)
Using fetch (native):
async function fetchUsers() {
try {
const response = await fetch('https://api.example.com/users');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const users = await response.json();
return users;
} catch (error) {
console.error('Failed to fetch users:', error);
return [];
}
}
With query parameters:
async function searchUsers(query) {
const params = new URLSearchParams({
q: query,
limit: 10,
sort: 'name'
});
const response = await fetch(`https://api.example.com/users?${params}`);
return response.json();
}
Using axios (library):
const axios = require('axios');
async function fetchUsers() {
try {
const response = await axios.get('https://api.example.com/users');
return response.data; // Axios automatically parses JSON
} catch (error) {
if (error.response) {
console.error('Server error:', error.response.status);
} else if (error.request) {
console.error('Network error');
} else {
console.error('Error:', error.message);
}
return [];
}
}
Handling different response types:
async function fetchData(url) {
const response = await fetch(url);
const contentType = response.headers.get('content-type');
if (contentType && contentType.includes('application/json')) {
return response.json();
} else {
throw new Error('Response is not JSON');
}
}
Tools: Check API responses with our HTTP Headers Checker and decode JWT tokens with our JWT Decoder.
Sending JSON in POST Requests
Basic POST request:
async function createUser(userData) {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
return response.json();
}
await createUser({
name: "John Doe",
email: "john@example.com"
});
With error handling:
async function createUser(userData) {
try {
const response = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(userData)
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.message || 'Request failed');
}
return await response.json();
} catch (error) {
console.error('Failed to create user:', error);
throw error;
}
}
With authentication:
async function apiRequest(url, data, token) {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(data)
});
return response.json();
}
Storing JSON in localStorage
Save to localStorage:
function saveToStorage(key, value) {
try {
const json = JSON.stringify(value);
localStorage.setItem(key, json);
return true;
} catch (error) {
if (error.name === 'QuotaExceededError') {
console.error('localStorage quota exceeded');
} else {
console.error('Failed to save:', error);
}
return false;
}
}
saveToStorage('user', {id: 123, name: "John"});
Read from localStorage:
function getFromStorage(key, defaultValue = null) {
try {
const item = localStorage.getItem(key);
if (item === null) {
return defaultValue;
}
return JSON.parse(item);
} catch (error) {
console.error('Failed to parse localStorage item:', error);
localStorage.removeItem(key); // Remove corrupted data
return defaultValue;
}
}
const user = getFromStorage('user');
Complete localStorage wrapper:
const storage = {
set(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch (error) {
console.error(`Failed to save "${key}":`, error);
return false;
}
},
get(key, defaultValue = null) {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : defaultValue;
} catch (error) {
console.error(`Failed to load "${key}":`, error);
return defaultValue;
}
},
remove(key) {
localStorage.removeItem(key);
},
clear() {
localStorage.clear();
}
};
// Usage
storage.set('user', {id: 123, name: "John"});
const user = storage.get('user');
JSON in Form Submissions
Convert form data to JSON:
function formToJson(form) {
const formData = new FormData(form);
const json = {};
for (const [key, value] of formData.entries()) {
// Handle multiple values (checkboxes, select multiple)
if (json[key]) {
if (!Array.isArray(json[key])) {
json[key] = [json[key]];
}
json[key].push(value);
} else {
json[key] = value;
}
}
return json;
}
// Usage
const form = document.querySelector('#myForm');
form.addEventListener('submit', async (e) => {
e.preventDefault();
const jsonData = formToJson(form);
const response = await fetch('/api/submit', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(jsonData)
});
});
Populate form from JSON:
function jsonToForm(form, data) {
for (const [key, value] of Object.entries(data)) {
const input = form.elements[key];
if (!input) continue;
if (input.type === 'checkbox') {
input.checked = Boolean(value);
} else if (input.type === 'radio') {
const radio = form.querySelector(`input[name="${key}"][value="${value}"]`);
if (radio) radio.checked = true;
} else {
input.value = value;
}
}
}
// Usage
const userData = {
name: "John Doe",
email: "john@example.com",
subscribe: true
};
jsonToForm(document.querySelector('#userForm'), userData);
Data Type Handling
Understanding how JSON handles different JavaScript types.
Strings, Numbers, Booleans
Strings:
JSON.stringify("hello"); // '"hello"'
JSON.stringify('hello'); // '"hello"'
JSON.stringify(`hello`); // '"hello"'
// Special characters are escaped
JSON.stringify("Line 1\nLine 2"); // '"Line 1\\nLine 2"'
JSON.stringify('He said "hi"'); // '"He said \\"hi\\""'
Numbers:
JSON.stringify(42); // '42'
JSON.stringify(3.14); // '3.14'
JSON.stringify(-17); // '-17'
JSON.stringify(1e10); // '10000000000'
// Special number values
JSON.stringify(NaN); // 'null'
JSON.stringify(Infinity); // 'null'
JSON.stringify(-Infinity); // 'null'
Booleans:
JSON.stringify(true); // 'true'
JSON.stringify(false); // 'false'
// In objects
JSON.stringify({active: true}); // '{"active":true}'
Arrays and Nested Arrays
Simple arrays:
JSON.stringify([1, 2, 3]);
// '[1,2,3]'
JSON.stringify(["a", "b", "c"]);
// '["a","b","c"]'
Mixed-type arrays:
JSON.stringify([1, "two", true, null]);
// '[1,"two",true,null]'
Nested arrays:
const matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
JSON.stringify(matrix);
// '[[1,2,3],[4,5,6],[7,8,9]]'
// With formatting
JSON.stringify(matrix, null, 2);
/*
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
*/
Arrays with objects:
const users = [
{id: 1, name: "John"},
{id: 2, name: "Jane"}
];
JSON.stringify(users, null, 2);
/*
[
{
"id": 1,
"name": "John"
},
{
"id": 2,
"name": "Jane"
}
]
*/
Objects and Nested Objects
Simple objects:
JSON.stringify({name: "John", age: 30});
// '{"name":"John","age":30}'
Nested objects:
const user = {
name: "John",
address: {
street: "123 Main St",
city: "New York",
coordinates: {
lat: 40.7128,
lng: -74.0060
}
}
};
JSON.stringify(user, null, 2);
Property order:
// JSON.stringify() may reorder properties
const obj = {z: 3, a: 1, m: 2};
JSON.stringify(obj);
// Order is not guaranteed! Could be any order.
Null vs Undefined
Null:
JSON.stringify(null); // 'null'
JSON.stringify({value: null}); // '{"value":null}'
JSON.stringify([null]); // '[null]'
Undefined:
JSON.stringify(undefined); // undefined (not a string!)
// In objects: property is omitted
JSON.stringify({a: 1, b: undefined, c: 3});
// '{"a":1,"c":3}'
// In arrays: becomes null
JSON.stringify([1, undefined, 3]);
// '[1,null,3]'
Handling undefined values:
// Convert undefined to null before stringifying
function sanitize(obj) {
return JSON.parse(JSON.stringify(obj, (key, value) => {
return value === undefined ? null : value;
}));
}
const data = {name: "John", age: undefined};
console.log(sanitize(data)); // {name: "John", age: null}
Dates in JSON (ISO 8601 Strings)
The problem:
const data = {
name: "John",
birthday: new Date('1990-01-15')
};
JSON.stringify(data);
// '{"name":"John","birthday":"1990-01-15T00:00:00.000Z"}'
// Date becomes a string!
const parsed = JSON.parse(JSON.stringify(data));
console.log(parsed.birthday instanceof Date); // false
console.log(typeof parsed.birthday); // "string"
Solution 1: Convert dates when parsing
const jsonString = '{"name":"John","birthday":"1990-01-15T00:00:00.000Z"}';
const data = JSON.parse(jsonString, (key, value) => {
// Detect ISO 8601 date strings
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value);
}
return value;
});
console.log(data.birthday instanceof Date); // true
Solution 2: Custom toJSON() method
Date.prototype.toJSON = function() {
return {
$date: this.toISOString()
};
};
const data = {birthday: new Date('1990-01-15')};
JSON.stringify(data);
// '{"birthday":{"$date":"1990-01-15T00:00:00.000Z"}}'
Solution 3: Store as timestamp
const data = {
name: "John",
birthday: new Date('1990-01-15').getTime() // Store as number
};
JSON.stringify(data);
// '{"name":"John","birthday":632188800000}'
// Convert back
const parsed = JSON.parse(JSON.stringify(data));
const birthday = new Date(parsed.birthday);
Tools: Format dates with our Timestamp Converter and generate ISO 8601 strings for JSON.
BigInt and Precision Issues
BigInt problem:
const data = {
id: 1234567890123456789n // BigInt
};
JSON.stringify(data);
// ❌ TypeError: Do not know how to serialize a BigInt
Solution: Convert to string
const data = {
id: 1234567890123456789n
};
const json = JSON.stringify(data, (key, value) => {
if (typeof value === 'bigint') {
return value.toString();
}
return value;
});
// '{"id":"1234567890123456789"}'
Number precision issues:
// JavaScript can't accurately represent all numbers
const data = {
id: 9007199254740993 // Beyond Number.MAX_SAFE_INTEGER
};
JSON.stringify(data);
// '{"id":9007199254740993}'
// But precision is lost!
const parsed = JSON.parse(JSON.stringify(data));
console.log(parsed.id === data.id); // false!
console.log(parsed.id); // 9007199254740992 (wrong!)
Solution: Store large numbers as strings
const data = {
id: "9007199254740993",
amount: "1234567890.123456789"
};
// Use a library like decimal.js for calculations
const Decimal = require('decimal.js');
const amount = new Decimal(data.amount);
Validation and Error Handling
Robust JSON handling requires comprehensive validation.
Checking if String is Valid JSON
Simple check:
function isValidJson(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
console.log(isValidJson('{"name":"John"}')); // true
console.log(isValidJson('{invalid}')); // false
With error details:
function validateJson(str) {
try {
const data = JSON.parse(str);
return {valid: true, data: data};
} catch (error) {
return {
valid: false,
error: error.message,
position: error.message.match(/position (\d+)/)?.[1]
};
}
}
const result = validateJson('{"name":"John"');
if (result.valid) {
console.log('Valid:', result.data);
} else {
console.log('Invalid at position', result.position);
}
Try/Catch Patterns
Basic pattern:
try {
const data = JSON.parse(jsonString);
console.log(data);
} catch (error) {
console.error('Parse error:', error.message);
}
With specific error handling:
try {
const data = JSON.parse(apiResponse);
processData(data);
} catch (error) {
if (error instanceof SyntaxError) {
console.error('Invalid JSON syntax');
} else {
console.error('Unexpected error:', error);
}
}
Async try/catch:
async function fetchAndParse(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
if (error.name === 'SyntaxError') {
console.error('Server returned invalid JSON');
} else if (error.message.includes('HTTP')) {
console.error('Server error:', error.message);
} else {
console.error('Network error:', error);
}
return null;
}
}
Schema Validation (AJV)
Install AJV:
npm install ajv ajv-formats
Basic validation:
const Ajv = require('ajv');
const addFormats = require('ajv-formats');
const ajv = new Ajv();
addFormats(ajv);
const schema = {
type: "object",
properties: {
name: {type: "string", minLength: 1},
email: {type: "string", format: "email"},
age: {type: "integer", minimum: 0}
},
required: ["name", "email"]
};
const validate = ajv.compile(schema);
const data = {
name: "John",
email: "john@example.com",
age: 30
};
if (validate(data)) {
console.log('Valid!');
} else {
console.log('Errors:', validate.errors);
}
Validate API responses:
async function fetchWithValidation(url, schema) {
const response = await fetch(url);
const data = await response.json();
const validate = ajv.compile(schema);
if (!validate(data)) {
throw new Error(`Invalid response: ${JSON.stringify(validate.errors)}`);
}
return data;
}
Tools: Learn more about JSON Schema in our JSON Schema Validation Tutorial.
Type Checking Parsed Data
Runtime type checking:
function validateUser(data) {
if (typeof data !== 'object' || data === null) {
throw new Error('User must be an object');
}
if (typeof data.name !== 'string') {
throw new Error('Name must be a string');
}
if (typeof data.age !== 'number' || data.age < 0) {
throw new Error('Age must be a positive number');
}
if (typeof data.email !== 'string' || !data.email.includes('@')) {
throw new Error('Email must be valid');
}
return true;
}
try {
const user = JSON.parse(apiResponse);
validateUser(user);
console.log('User is valid');
} catch (error) {
console.error('Validation failed:', error.message);
}
Type guard functions:
function isUser(obj) {
return (
typeof obj === 'object' &&
obj !== null &&
typeof obj.name === 'string' &&
typeof obj.age === 'number' &&
typeof obj.email === 'string'
);
}
const data = JSON.parse(apiResponse);
if (isUser(data)) {
console.log(data.name); // TypeScript knows this is safe
}
Defensive Programming Practices
Always validate external data:
async function getUserData(userId) {
const response = await fetch(`/api/users/${userId}`);
// Check content type
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Response is not JSON');
}
// Check status
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
// Validate structure
if (!data || typeof data !== 'object') {
throw new Error('Invalid response structure');
}
return data;
}
Provide defaults:
function parseUserData(jsonString) {
try {
const data = JSON.parse(jsonString);
return {
name: data.name || 'Unknown',
email: data.email || '',
age: typeof data.age === 'number' ? data.age : 0,
active: Boolean(data.active)
};
} catch (error) {
console.error('Parse failed, returning defaults');
return {
name: 'Unknown',
email: '',
age: 0,
active: false
};
}
}
Tools: Validate JSON structure with our JSON Formatter and debug errors with our 15 Common JSON Errors Guide.
Performance Optimization
Optimize JSON operations for large datasets and production use.
Parsing Large JSON Files
Stream large files (Node.js):
const fs = require('fs');
const JSONStream = require('JSONStream');
function streamLargeJson(filename) {
return new Promise((resolve, reject) => {
const stream = fs.createReadStream(filename);
const parser = JSONStream.parse('*');
const items = [];
stream.pipe(parser)
.on('data', (item) => {
items.push(item);
})
.on('end', () => resolve(items))
.on('error', reject);
});
}
Process in chunks:
async function processLargeArray(items, chunkSize = 1000) {
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
// Process chunk
await processChunk(chunk);
// Allow event loop to breathe
await new Promise(resolve => setImmediate(resolve));
}
}
Lazy parsing:
// Only parse what you need
const text = await fetch('/api/large-data').then(r => r.text());
// Extract just the part you need
const match = text.match(/"users":\s*\[([^\]]+)\]/);
if (match) {
const users = JSON.parse(`[${match[1]}]`);
}
Streaming JSON Data
Using JSON.parse() with streaming (Node.js):
const { pipeline } = require('stream');
const JSONStream = require('JSONStream');
async function streamProcessJson(inputFile, outputFile) {
const readStream = fs.createReadStream(inputFile);
const parseStream = JSONStream.parse('items.*');
const writeStream = fs.createWriteStream(outputFile);
const transformStream = new Transform({
objectMode: true,
transform(item, encoding, callback) {
// Process each item
const processed = {
...item,
processed: true,
timestamp: Date.now()
};
callback(null, JSON.stringify(processed) + '\n');
}
});
await pipeline(
readStream,
parseStream,
transformStream,
writeStream
);
}
Memory Management
Avoid memory leaks:
// ❌ BAD - Creates huge string in memory
const data = {
items: new Array(1000000).fill({...})
};
const json = JSON.stringify(data); // May crash!
// ✅ GOOD - Process in batches
const batches = [];
for (let i = 0; i < data.items.length; i += 1000) {
const batch = data.items.slice(i, i + 1000);
batches.push(JSON.stringify(batch));
}
Release references:
async function processLargeFile(filename) {
let data = JSON.parse(await fs.readFile(filename, 'utf8'));
await processData(data);
data = null; // Release memory
if (global.gc) global.gc(); // Force GC (if --expose-gc flag)
}
When to Use JSON vs Alternatives
Use JSON when:
- ✅ Data needs to be human-readable
- ✅ Working with web APIs
- ✅ Data size is reasonable (< 10MB)
- ✅ Cross-language compatibility needed
Use alternatives when:
- ❌ Data is > 100MB (use streaming formats)
- ❌ Need faster parsing (use Protocol Buffers, MessagePack)
- ❌ Binary data (use ArrayBuffer, Blob)
- ❌ Need compression (use BSON, CBOR)
Alternatives:
| Format | Use Case | Speed | Size |
|---|---|---|---|
| JSON | General purpose | Medium | Large |
| MessagePack | Fast binary | Fast | Small |
| Protocol Buffers | Typed binary | Very Fast | Very Small |
| BSON | MongoDB | Medium | Medium |
| CBOR | IoT, embedded | Fast | Small |
Benchmarks and Best Practices
Benchmark JSON operations:
const { performance } = require('perf_hooks');
function benchmark(fn, iterations = 10000) {
const start = performance.now();
for (let i = 0; i < iterations; i++) {
fn();
}
const end = performance.now();
return end - start;
}
const data = {name: "John", age: 30, hobbies: ["reading", "gaming"]};
console.log('stringify:', benchmark(() => JSON.stringify(data)), 'ms');
console.log('parse:', benchmark(() => JSON.parse(JSON.stringify(data))), 'ms');
Best practices:
- Compile validators once, reuse many times
- Use streaming for large files
- Cache parsed results when possible
- Validate in development, skip in production (if data is trusted)
- Use workers for heavy JSON processing
// Use Web Workers for heavy JSON parsing
const worker = new Worker('json-worker.js');
worker.postMessage(largeJsonString);
worker.onmessage = (e) => {
const parsed = e.data;
console.log('Parsed in worker:', parsed);
};
Common Mistakes and How to Fix Them
Let’s debug the most frequent JSON errors in JavaScript.
SyntaxError: Unexpected Token
Error:
const json = '{"name": "John", "age": 30,}'; // Trailing comma
JSON.parse(json);
// ❌ SyntaxError: Unexpected token } in JSON
Fix:
// Remove trailing comma
const json = '{"name": "John", "age": 30}';
JSON.parse(json); // ✅ Works
Tools: Debug syntax errors with our JSON Formatter.
Undefined is Not Valid JSON
Error:
const data = {
name: "John",
age: undefined
};
JSON.stringify(data);
// '{"name":"John"}' - age is silently removed!
Fix:
// Use null instead of undefined
const data = {
name: "John",
age: null
};
JSON.stringify(data);
// '{"name":"John","age":null}'
Losing Data Precision
Problem:
const data = {
id: 9007199254740993 // Larger than Number.MAX_SAFE_INTEGER
};
const json = JSON.stringify(data);
const parsed = JSON.parse(json);
console.log(parsed.id === data.id); // false! Precision lost
Fix:
// Store large numbers as strings
const data = {
id: "9007199254740993"
};
// Or use a library like decimal.js or bignumber.js
Circular Structure Errors
Error:
const obj = {name: "John"};
obj.self = obj;
JSON.stringify(obj);
// ❌ TypeError: Converting circular structure to JSON
Fix: See the “Handling Circular References” section above.
Character Encoding Issues
Problem:
// UTF-8 characters may break
const data = {message: "Hello 世界"};
const json = JSON.stringify(data); // Works in modern browsers
// But reading from files...
const content = fs.readFileSync('data.json', 'latin1'); // ❌ Wrong encoding
JSON.parse(content); // Garbled characters
Fix:
// Always use UTF-8
const content = fs.readFileSync('data.json', 'utf8');
JSON.parse(content); // ✅ Correct
Trailing Commas Breaking JSON
See Error #4 in our JSON Debugging Guide.
Advanced Techniques
Power user patterns for complex JSON scenarios.
Custom toJSON() Method
Define how objects serialize:
class User {
constructor(name, password) {
this.name = name;
this.password = password;
}
toJSON() {
// Don't include password in JSON
return {
name: this.name,
type: 'User'
};
}
}
const user = new User('John', 'secret123');
JSON.stringify(user);
// '{"name":"John","type":"User"}'
Handle dates custom:
Date.prototype.toJSON = function() {
return {
$date: this.getTime()
};
};
JSON with Functions (Workarounds)
Problem: Functions are lost:
const obj = {
name: "Calculator",
add: function(a, b) { return a + b; }
};
JSON.stringify(obj);
// '{"name":"Calculator"}' - function is gone!
Workaround 1: Store as string, eval later (dangerous!):
const obj = {
name: "Calculator",
add: "function(a, b) { return a + b; }"
};
const json = JSON.stringify(obj);
const parsed = JSON.parse(json);
// Restore function (unsafe!)
parsed.add = eval(`(${parsed.add})`);
Workaround 2: Use Function constructor (still risky):
parsed.add = new Function('a', 'b', 'return a + b');
Better: Don’t store functions, store configuration:
const obj = {
name: "Calculator",
operation: "add" // Store operation name
};
// Reconstruct behavior on client
const operations = {
add: (a, b) => a + b,
subtract: (a, b) => a - b
};
const parsed = JSON.parse(json);
const fn = operations[parsed.operation];
JSON Patches (RFC 6902)
Apply changes to JSON documents:
const jsonpatch = require('fast-json-patch');
const original = {
name: "John",
age: 30
};
const modified = {
name: "John Doe",
age: 30,
email: "john@example.com"
};
// Generate patch
const patch = jsonpatch.compare(original, modified);
console.log(patch);
/*
[
{ op: 'replace', path: '/name', value: 'John Doe' },
{ op: 'add', path: '/email', value: 'john@example.com' }
]
*/
// Apply patch
const result = jsonpatch.applyPatch(original, patch).newDocument;
Working with JSON Lines (.jsonl)
JSON Lines format: One JSON object per line
{"name":"John","age":30}
{"name":"Jane","age":25}
{"name":"Bob","age":35}
Reading JSON Lines:
const fs = require('fs');
function readJsonLines(filename) {
const content = fs.readFileSync(filename, 'utf8');
const lines = content.trim().split('\n');
return lines.map(line => JSON.parse(line));
}
const data = readJsonLines('data.jsonl');
Writing JSON Lines:
function writeJsonLines(filename, items) {
const lines = items.map(item => JSON.stringify(item)).join('\n');
fs.writeFileSync(filename, lines, 'utf8');
}
Streaming JSON Lines:
const readline = require('readline');
async function streamJsonLines(filename) {
const fileStream = fs.createReadStream(filename);
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
for await (const line of rl) {
const item = JSON.parse(line);
console.log(item);
}
}
Real-World Examples
Complete, production-ready patterns.
Fetching User Data from API
async function getUserProfile(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error('User not found');
}
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Response is not JSON');
}
const user = await response.json();
// Validate response structure
if (!user.id || !user.name) {
throw new Error('Invalid user data structure');
}
return {
success: true,
data: user
};
} catch (error) {
console.error('Failed to fetch user:', error);
return {
success: false,
error: error.message
};
}
}
// Usage
const result = await getUserProfile(123);
if (result.success) {
console.log('User:', result.data.name);
} else {
console.error('Error:', result.error);
}
Form Data to JSON
class FormSerializer {
constructor(form) {
this.form = form;
}
toJSON() {
const formData = new FormData(this.form);
const json = {};
for (const [key, value] of formData.entries()) {
this.addValue(json, key, value);
}
return json;
}
addValue(obj, key, value) {
// Handle array notation: name[]
if (key.endsWith('[]')) {
const arrayKey = key.slice(0, -2);
if (!obj[arrayKey]) {
obj[arrayKey] = [];
}
obj[arrayKey].push(value);
}
// Handle nested notation: user[name]
else if (key.includes('[')) {
this.addNestedValue(obj, key, value);
}
// Simple key
else {
if (obj[key]) {
// Multiple values with same key
if (!Array.isArray(obj[key])) {
obj[key] = [obj[key]];
}
obj[key].push(value);
} else {
obj[key] = value;
}
}
}
addNestedValue(obj, key, value) {
const parts = key.split(/\[|\]/).filter(Boolean);
let current = obj;
for (let i = 0; i < parts.length - 1; i++) {
if (!current[parts[i]]) {
current[parts[i]] = {};
}
current = current[parts[i]];
}
current[parts[parts.length - 1]] = value;
}
}
// Usage
const form = document.querySelector('#userForm');
const serializer = new FormSerializer(form);
const json = serializer.toJSON();
fetch('/api/users', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(json)
});
LocalStorage Cache Implementation
class JSONCache {
constructor(prefix = 'cache_') {
this.prefix = prefix;
}
set(key, value, ttl = 3600000) { // Default 1 hour
const item = {
value: value,
expires: Date.now() + ttl
};
try {
localStorage.setItem(this.prefix + key, JSON.stringify(item));
return true;
} catch (error) {
console.error('Cache set failed:', error);
return false;
}
}
get(key) {
try {
const itemStr = localStorage.getItem(this.prefix + key);
if (!itemStr) {
return null;
}
const item = JSON.parse(itemStr);
// Check expiration
if (Date.now() > item.expires) {
this.delete(key);
return null;
}
return item.value;
} catch (error) {
console.error('Cache get failed:', error);
this.delete(key);
return null;
}
}
delete(key) {
localStorage.removeItem(this.prefix + key);
}
clear() {
const keys = Object.keys(localStorage);
for (const key of keys) {
if (key.startsWith(this.prefix)) {
localStorage.removeItem(key);
}
}
}
}
// Usage
const cache = new JSONCache();
cache.set('user_123', {name: "John", email: "john@example.com"}, 600000);
const user = cache.get('user_123');
if (user) {
console.log('From cache:', user);
} else {
// Fetch fresh data
}
Building a JSON API Client
class APIClient {
constructor(baseURL, options = {}) {
this.baseURL = baseURL;
this.defaultHeaders = {
'Content-Type': 'application/json',
...options.headers
};
this.timeout = options.timeout || 30000;
}
async request(endpoint, options = {}) {
const url = `${this.baseURL}${endpoint}`;
const config = {
...options,
headers: {
...this.defaultHeaders,
...options.headers
}
};
// Add timeout
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
config.signal = controller.signal;
try {
const response = await fetch(url, config);
clearTimeout(timeoutId);
// Parse JSON response
const contentType = response.headers.get('content-type');
let data;
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = await response.text();
}
if (!response.ok) {
throw new APIError(response.status, data.message || 'Request failed', data);
}
return data;
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new Error('Request timeout');
}
throw error;
}
}
async get(endpoint, params = {}) {
const queryString = new URLSearchParams(params).toString();
const url = queryString ? `${endpoint}?${queryString}` : endpoint;
return this.request(url, {method: 'GET'});
}
async post(endpoint, data) {
return this.request(endpoint, {
method: 'POST',
body: JSON.stringify(data)
});
}
async put(endpoint, data) {
return this.request(endpoint, {
method: 'PUT',
body: JSON.stringify(data)
});
}
async delete(endpoint) {
return this.request(endpoint, {method: 'DELETE'});
}
}
class APIError extends Error {
constructor(status, message, data) {
super(message);
this.status = status;
this.data = data;
}
}
// Usage
const api = new APIClient('https://api.example.com', {
headers: {
'Authorization': 'Bearer token123'
},
timeout: 10000
});
try {
const users = await api.get('/users', {limit: 10, sort: 'name'});
const newUser = await api.post('/users', {name: "John", email: "john@example.com"});
} catch (error) {
if (error instanceof APIError) {
console.error(`API Error ${error.status}:`, error.message);
} else {
console.error('Request failed:', error);
}
}
Frequently Asked Questions
What’s the difference between JSON and JavaScript objects?
JSON is a text format (a string). JavaScript objects are data structures in memory.
// JSON (string)
const json = '{"name":"John"}'; // This is text
// JavaScript object
const obj = {name: "John"}; // This is a data structure
Key differences:
- JSON uses double quotes only
- JSON has no functions
- JSON has no undefined
- JSON is language-independent
Why does JSON.stringify() return undefined?
JSON.stringify() returns undefined in these cases:
JSON.stringify(undefined); // undefined (not a string!)
JSON.stringify(function() {}); // undefined
JSON.stringify(Symbol('id')); // undefined
For objects:
JSON.stringify({a: undefined}); // '{}' (property omitted)
Solution: Check the input before stringifying.
How do I handle dates in JSON?
Problem: Dates become strings:
const data = {date: new Date()};
JSON.stringify(data); // Date is now a string!
Solution: Use a reviver when parsing:
JSON.parse(jsonString, (key, value) => {
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T/.test(value)) {
return new Date(value);
}
return value;
});
Or store as timestamp:
const data = {date: Date.now()}; // Store as number
Can I use comments in JSON?
No. JSON does not support comments.
// ❌ Invalid JSON
{
// This is a comment
"name": "John"
}
Workarounds:
- Use a separate documentation file
- Add a
"_comment"property:
{
"_comment": "This is user configuration",
"name": "John"
}
- Use JSON5 (superset that allows comments)
How do I parse JSON with duplicate keys?
const json = '{"name":"John","age":30,"name":"Jane"}';
const obj = JSON.parse(json);
console.log(obj.name); // "Jane" (last value wins)
JSON.parse() silently uses the last value. To detect duplicates, parse manually or use a strict validator.
Tools: Learn about JSON Schema validation in our complete tutorial.
What’s the maximum JSON size I can parse?
Limits vary by environment:
- Browsers: Typically 100MB-500MB
- Node.js: Limited by heap size (default ~1.5GB)
- Mobile: Much smaller (50MB-100MB)
Best practices:
- Keep JSON under 10MB for web apps
- Use streaming for larger files
- Consider pagination for large datasets
Conclusion: Master JSON in JavaScript
You’ve now learned everything you need to work with JSON in JavaScript effectively. Let’s recap:
Core concepts:
- JSON vs JavaScript objects - JSON is a string format, not a data structure
- JSON.parse() - Convert JSON strings to JavaScript objects
- JSON.stringify() - Convert JavaScript to JSON strings
- Error handling - Always use try/catch with external data
- Type handling - Understand how undefined, dates, and special values work
- Performance - Stream large files, cache parsed results
- Validation - Check data before using it
Key takeaways:
- JSON is always a string until parsed
- Functions and undefined are not valid in JSON
- Always validate external JSON data
- Use proper error handling
- Understand the limitations (circular references, precision, dates)
Your JSON JavaScript Checklist:
Before parsing:
- ✅ Verify content-type is application/json
- ✅ Wrap JSON.parse() in try/catch
- ✅ Validate response status codes
- ✅ Check for empty or invalid responses
When stringifying:
- ✅ Handle circular references
- ✅ Convert undefined to null
- ✅ Format dates consistently
- ✅ Remove sensitive data
In production:
- ✅ Use schema validation
- ✅ Implement proper error handling
- ✅ Cache parsed results when possible
- ✅ Stream large JSON files
- ✅ Monitor for parsing errors
Your JSON Toolkit
Essential tools for JSON work:
Validation & Debugging:
- JSON Formatter - Validate, format, and debug JSON
- Diff Tool - Compare JSON objects
- JSON Schema Tutorial - Learn validation
Error Fixing:
- 15 Common JSON Errors Guide - Debug issues
- Regex Tester - Test JSON patterns
Development Tools:
- Converter - Convert between formats
- Encoder/Decoder - Base64 encoding
- UUID Generator - Generate unique IDs
- Timestamp Converter - Format dates
API Tools:
- HTTP Headers Checker - Debug API responses
- JWT Decoder - Decode tokens
View all tools: Developer Tools
Next Steps
- Practice with our JSON Formatter
- Implement validation in your projects
- Add error handling to all JSON operations
- Learn JSON Schema with our complete guide
- Share this guide with your team
Additional Resources
Official Documentation:
Libraries:
- AJV - JSON Schema validator
- json-bigint - Handle large numbers
- flatted - Handle circular references
Related Articles:
- 50 JSON API Response Examples - Real-world patterns
- JSON vs XML vs YAML - Choose formats
- Base64 Encoding Guide - Encode binary data
Found this helpful? Share it with your team and bookmark our developer tools for your next project.
Have questions about JSON? Contact us or check our FAQ for more help.
Last Updated: November 1, 2025
Reading Time: 19 minutes
Author: Orbit2x Team

