JSON is used everywhere: APIs, configs, and data exchange. Invalid or unreadable JSON causes hard-to-debug errors. Here are practical habits for formatting, validating, and working with JSON safely.
JSON syntax basics
Valid JSON must follow these rules:
| Rule | Valid | Invalid |
|---|---|---|
| Keys | "name" |
name or 'name' |
| Strings | "value" |
'value' |
| Trailing commas | Not allowed | {"a": 1,} |
| Comments | Not allowed | // comment |
| Numbers | 123 or 12.5 |
123. or .5 |
| Boolean | true / false |
True / FALSE |
| Null | null |
NULL or undefined |
Valid JSON example
{
"name": "John Doe",
"age": 30,
"email": "john@example.com",
"isActive": true,
"roles": ["admin", "user"],
"metadata": null,
"address": {
"city": "New York",
"zip": "10001"
}
}
Common errors
Trailing commas
// Invalid
{
"name": "John",
"age": 30,
}
// Valid
{
"name": "John",
"age": 30
}
Single quotes
// Invalid
{'name': 'John'}
// Valid
{"name": "John"}
Unquoted keys
// Invalid
{name: "John"}
// Valid
{"name": "John"}
Comments
// Invalid - comments not allowed
{
"name": "John" // user's name
}
// Valid - no comments
{
"name": "John"
}
Undefined
// Invalid - undefined is not valid JSON
{"value": undefined}
// Valid - use null instead
{"value": null}
Validation
JavaScript
function parseJSON(text: string): unknown | null {
try {
return JSON.parse(text);
} catch (error) {
console.error('Invalid JSON:', error.message);
return null;
}
}
const data = parseJSON('{"name": "John"}');
if (data) {
// Valid JSON
}
With error location
function validateJSON(text: string): { valid: boolean; error?: string } {
try {
JSON.parse(text);
return { valid: true };
} catch (e) {
const error = e as SyntaxError;
return {
valid: false,
error: error.message,
};
}
}
Schema validation with Zod
import { z } from 'zod';
const userSchema = z.object({
name: z.string(),
email: z.string().email(),
age: z.number().min(0),
});
const result = userSchema.safeParse(JSON.parse(jsonString));
if (!result.success) {
console.error(result.error.issues);
}
Formatting
Prettify (indent)
const obj = { name: "John", age: 30 };
// 2-space indent
JSON.stringify(obj, null, 2);
// 4-space indent
JSON.stringify(obj, null, 4);
// Tab indent
JSON.stringify(obj, null, '\t');
Minify
JSON.stringify(obj);
// {"name":"John","age":30}
Sorted keys
function sortedStringify(obj: unknown): string {
return JSON.stringify(obj, (key, value) => {
if (value && typeof value === 'object' && !Array.isArray(value)) {
return Object.keys(value)
.sort()
.reduce((sorted, k) => {
sorted[k] = value[k];
return sorted;
}, {} as Record<string, unknown>);
}
return value;
}, 2);
}
Working with JSON
Parse safely
function safeJSONParse<T>(
text: string,
fallback: T
): T {
try {
return JSON.parse(text) as T;
} catch {
return fallback;
}
}
const config = safeJSONParse(rawConfig, { debug: false });
Deep clone
function deepClone<T>(obj: T): T {
return JSON.parse(JSON.stringify(obj));
}
Note: This loses functions, undefined, Date objects, etc.
Check if valid JSON string
function isJSON(text: string): boolean {
try {
JSON.parse(text);
return true;
} catch {
return false;
}
}
JSON vs JavaScript objects
| Feature | JSON | JS Object |
|---|---|---|
| Keys | Must be quoted | Can be unquoted |
| Strings | Double quotes only | Single or double |
| Trailing commas | Invalid | Valid |
| Comments | Invalid | Valid |
| Functions | Invalid | Valid |
| undefined | Invalid | Valid |
| Date | String only | Date object |
Converting
// Object to JSON string
const json = JSON.stringify(obj);
// JSON string to object
const obj = JSON.parse(json);
// With reviver for dates
const obj = JSON.parse(json, (key, value) => {
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}/.test(value)) {
return new Date(value);
}
return value;
});
JSONC (JSON with Comments)
For configuration files, use JSONC:
// tsconfig.json (JSONC)
{
"compilerOptions": {
"target": "ES2020", // Modern target
"strict": true // Enable all strict checks
}
}
Parse JSONC by stripping comments:
function parseJSONC(text: string): unknown {
// Remove single-line comments
const withoutComments = text.replace(/\/\/.*$/gm, '');
// Remove multi-line comments
const clean = withoutComments.replace(/\/\*[\s\S]*?\*\//g, '');
return JSON.parse(clean);
}
JSONPath
Query JSON data with JSONPath:
// Using jsonpath-plus library
import { JSONPath } from 'jsonpath-plus';
const data = {
store: {
books: [
{ title: "Book 1", price: 10 },
{ title: "Book 2", price: 20 },
],
},
};
// Get all book titles
JSONPath({ path: '$.store.books[*].title', json: data });
// ["Book 1", "Book 2"]
// Get books under $15
JSONPath({ path: '$.store.books[?(@.price < 15)]', json: data });
Tools
Browser tools
- JSON Formatter & Validator - Format and validate JSON
- JSON to TypeScript - Generate types from JSON
- JSON Path Tester - Test JSONPath queries
IDE features
Most IDEs format JSON:
- VS Code: Right-click → Format Document
- JetBrains: Code → Reformat Code
Command line
# jq - format and query JSON
echo '{"name":"John"}' | jq '.'
# Python - pretty print
python -m json.tool file.json
Best practices
| Practice | Why |
|---|---|
| Validate before parsing | Catch errors early |
| Use try/catch | Handle invalid JSON gracefully |
| Prettify for debugging | Easier to read and diff |
| Minify for production | Smaller payload |
| Use TypeScript types | Catch type errors |
| Validate with schemas | Ensure data structure |
Summary
For reliable JSON handling:
- Know the syntax - Double quotes, no trailing commas, no comments
- Validate first - Wrap
JSON.parsein try/catch - Use formatting - Prettify for debugging, minify for production
- Use schema validation - Zod or similar for type safety
- Use tools - Formatters and validators save time
For quick formatting and validation in the browser, use the JSON Formatter & Validator tool.
