JSON Tips That Will Make You a Better Developer
JSON is everywhere. Most developers use 10% of what it can do.
Beyond JSON.parse and JSON.stringify
1. Pretty Print
const data = { name: "Alex", age: 30, skills: ["js", "python", "go"] };
// Compact (default)
JSON.stringify(data); // '{"name":"Alex","age":30,"skills":["js","python","go"]}'
// Pretty print
JSON.stringify(data, null, 2);
// {
// "name": "Alex",
// "age": 30,
// "skills": [
// "js",
// "python",
// "go"
// ]
// }
2. Filter Keys with Replacer
const user = {
id: 'abc123',
name: 'Alex',
email: 'alex@example.com',
password: 'supersecret123',
creditCard: '4111-1111-1111-1111',
};
// Only include safe fields
const safe = JSON.stringify(user, ['id', 'name', 'email'], 2);
// Remove sensitive fields
const clean = JSON.stringify(user, (key, value) => {
if (['password', 'creditCard', 'ssn', 'token'].includes(key)) return undefined;
return value;
}, 2);
3. Deep Clone
const original = { a: 1, b: { c: 2 } };
// Deep clone using JSON (works for JSON-serializable data)
const clone = JSON.parse(JSON.stringify(original));
clone.b.c = 99;
console.log(original.b.c); // Still 2 — truly independent!
4. Reviver: Transform While Parsing
const json = '{"name":"Alex","age":"30","active":"true","created":"2026-01-15"}';
const parsed = JSON.parse(json, (key, value) => {
if (key === 'age') return Number(value); // String → Number
if (key === 'active') return Boolean(value); // String → Boolean
if (key === 'created') return new Date(value); // String → Date
return value;
});
// parsed.age is a number, parsed.active is a boolean, parsed.created is a Date object
5. Circular Reference Handling
const obj = { name: 'Alex' };
obj.self = obj; // Circular!
// This throws: TypeError: Converting circular structure to JSON
// JSON.stringify(obj);
// Fix: Use a replacer that tracks seen objects
function safeStringify(obj, indent = 2) {
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;
}, indent);
}
console.log(safeStringify(obj));
// {"name":"Alex","self":"[Circular]"}
6. JSON Schema Validation
// Validate JSON structure with Zod (easier than JSON Schema)
import { z } from 'zod';
const UserSchema = z.object({
name: z.string().min(1),
age: z.number().int().min(0).max(150),
email: z.string().email(),
tags: z.array(z.string()).optional(),
metadata: z.record(z.unknown()).optional(), // Any JSON object
});
const result = UserSchema.safeParse(JSON.parse(jsonString));
if (!result.success) {
console.error(result.error); // Field-level errors
}
7. JSON Lines (NDJSON)
// One JSON object per line (used in logs, streaming)
// file.ndjson:
// {"level":"info","msg":"Server started","time":"2026-01-15T00:00:00Z"}
// {"level":"error","msg":"Connection failed","time":"2026-01-15T00:01:00Z"}
import { readFileSync, writeFileSync } from 'fs';
// Read
const lines = readFileSync('file.ndjson', 'utf8').trim().split('\n');
const records = lines.map(line => JSON.parse(line));
// Filter and process
const errors = records.filter(r => r.level === 'error');
// Write
const newRecords = [{ level: 'info', msg: 'Processed', time: new Date().toISOString() }];
writeFileSync('output.ndjson', newRecords.map(r => JSON.stringify(r)).join('\n') + '\n');
8. JSON Merge (Deep)
// Shallow merge (Object.assign)
const merged = { ...defaults, ...userConfig }; // userConfig overrides defaults
// Deep merge utility
function deepMerge(target, ...sources) {
for (const source of sources) {
for (const key of Object.keys(source)) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
if (!target[key]) target[key] = {};
deepMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
}
}
return target;
}
const config = deepMerge(
{ api: { timeout: 5000, retries: 3 }, debug: false },
{ api: { timeout: 10000 }, debug: true }
);
// { api: { timeout: 10000, retries: 3 }, debug: true }
9. JSON to URL Query String
const params = { q: 'node.js tutorial', page: 1, limit: 20 };
const queryString = new URLSearchParams(params).toString();
// "q=node.js+tutorial&page=1&limit=20"
fetch(`/api/search?${queryString}`);
10. JSONC — JSON with Comments
// tsconfig.json is actually JSONC (JSON with Comments)
{
// Compiler options
"compilerOptions": {
"target": "ES2022", /* Target ECMAScript version */
"strict": true, // Enable all strict checks
}
/* This is a block comment */
}
// Parse JSONC (strip comments first)
function parseJSONC(text) {
// Remove single-line comments
const stripped = text.replace(/\/\/.*$/gm, '');
// Remove block comments
const cleaned = stripped.replace(/\/\*[\s\S]*?\*\//g, '');
return JSON.parse(cleaned);
}
Performance Tips
// JSON.parse is fast. But for repeated parsing of the same data, cache it:
const cache = new Map();
function getParsedConfig() {
if (!cache.has('config')) {
cache.set('config', JSON.parse(readFileSync('config.json', 'utf8')));
}
return cache.get('config');
}
// For very large JSON files (>100MB), consider streaming:
import { createReadStream } from 'fs';
import { createInterface } from 'readline';
async function* parseLargeJSON(filePath) {
const stream = createReadStream(filePath, { encoding: 'utf8' });
const rl = createInterface({ input: stream });
let buffer = '';
let depth = 0;
for await (const chunk of rl) {
buffer += chunk;
for (const char of buffer) {
if (char === '{' || char === '[') depth++;
if (char === '}' || char === ']') depth--;
if (depth === 0 && buffer.trim()) {
yield JSON.parse(buffer.trim());
buffer = '';
}
}
}
}
What's your favorite JSON trick? Anything I missed?
Follow @armorbreak for more developer tips.
Top comments (0)