JSON (JavaScript Object Notation) is the universal language of web APIs and modern data interchange. From debugging API responses to configuring applications, properly formatted and validated JSON is essential. Let's master JSON formatting, validation, and best practices for professional development.
Why JSON Formatting & Validation Matters
The JSON Problem
// The universal data exchange challenge
const jsonReality = {
ubiquity: 'Every API uses JSON',
problem: 'Malformed JSON breaks everything',
breakageExamples: {
missingComma: {
json: '{"name": "John" "age": 30}', // Missing comma
result: 'SyntaxError: Unexpected string',
impact: 'API call fails, app crashes'
},
trailingComma: {
json: '{"name": "John", "age": 30,}', // Trailing comma
result: 'SyntaxError (in strict parsers)',
impact: 'Mobile apps crash, data lost'
},
singleQuotes: {
json: "{'name': 'John'}", // Single quotes invalid
result: 'SyntaxError: Unexpected token',
impact: 'Config file rejected'
},
unquotedKeys: {
json: '{name: "John"}', // Key not quoted
result: 'Invalid JSON',
impact: 'Cannot parse, system fails'
}
},
solution: 'Format + Validate = Zero JSON errors',
impact: 'Saves hours of debugging daily'
};
console.log('JSON: The universal data format that must be perfect');
Real-World Impact
// Production incidents caused by bad JSON
const realIncidents = [
{
company: 'E-commerce platform',
year: 2023,
issue: 'Config file with trailing comma',
impact: 'Deployment failed, 2-hour outage',
cost: '$50,000 in lost sales',
fix: 'JSON validator in CI/CD pipeline'
},
{
company: 'Mobile banking app',
year: 2024,
issue: 'API returning invalid JSON (NaN value)',
impact: '10,000 app crashes',
cost: '1-star reviews, customer churn',
fix: 'Strict JSON validation on API responses'
},
{
company: 'SaaS platform',
year: 2023,
issue: 'User-submitted JSON with unescaped quotes',
impact: 'Data corruption, 500 accounts affected',
cost: '$100,000 in recovery + compensation',
fix: 'JSON validation on all inputs'
}
];
// Before validation: Hours wasted on syntax errors
const beforeValidation = {
debugging: '2-3 hours finding missing comma',
frustration: 'High - "Where is the syntax error?"',
deployment: 'Fails in production (no pre-check)',
confidence: 'Low - never sure JSON is valid'
};
// With validation: Instant error detection
const withValidation = {
debugging: '2 seconds - validator shows exact error',
frustration: 'Zero - clear error messages',
deployment: 'Safe - validated before deploy',
confidence: 'High - guaranteed valid JSON'
};
// The cost of bad JSON
const badJSONCost = {
timeCost: '30 minutes per error × 5 errors/week = 2.5 hours/week',
yearlyCost: '2.5 hours × 50 weeks × $100/hour = $12,500/year per developer',
organizationCost: '$12,500 × 50 developers = $625,000/year',
validationCost: '$0 (built-in tools)',
savings: '$625,000/year'
};
console.log('JSON validation: Free tool, massive savings');
JSON Syntax Rules
Valid JSON Requirements
// The complete JSON specification (RFC 8259)
const jsonRules = {
dataTypes: {
string: '"Hello" - Must use double quotes',
number: '42, 3.14, -10, 2.5e3 - No NaN or Infinity',
boolean: 'true, false - Lowercase only',
null: 'null - Lowercase only',
object: '{"key": "value"} - Keys must be strings',
array: '[1, 2, 3] - Ordered list'
},
stringRules: {
quotes: 'MUST use double quotes "text"',
escape: 'Must escape: \\" \\\\ \\/ \\b \\f \\n \\r \\t \\uXXXX',
invalid: 'Cannot use single quotes or unescaped quotes'
},
objectRules: {
keys: 'MUST be strings in double quotes',
separator: 'Colon : between key and value',
delimiter: 'Comma , between pairs',
noTrailing: 'NO trailing comma after last pair',
unique: 'Duplicate keys allowed but discouraged'
},
arrayRules: {
delimiter: 'Comma , between elements',
noTrailing: 'NO trailing comma after last element',
mixed: 'Can contain different types'
},
whitespace: {
allowed: 'Space, tab, newline, carriage return',
ignored: 'Whitespace outside strings is ignored',
use: 'For formatting/readability only'
},
forbidden: {
comments: 'JSON does NOT support comments',
functions: 'Cannot include functions',
undefined: 'Cannot use undefined',
NaN: 'Cannot use NaN',
Infinity: 'Cannot use Infinity',
dates: 'No native Date type (use ISO strings)',
regex: 'No RegExp type'
}
};
// Valid JSON examples
const validJSON = {
string: '"Hello, World!"',
number: '42',
boolean: 'true',
null: 'null',
array: '[1, 2, 3]',
object: '{"name": "John", "age": 30}',
nested: '{"user": {"name": "John", "scores": [95, 87, 92]}}',
escaped: '{"text": "Line 1\\nLine 2\\t\\"quoted\\""}'
};
// Invalid JSON examples (common mistakes)
const invalidJSON = {
singleQuotes: "{'name': 'John'}", // ✗ Must use double quotes
unquotedKey: '{name: "John"}', // ✗ Keys must be quoted
trailingComma: '{"name": "John",}', // ✗ No trailing commas
comments: '{"name": "John" /* comment */}', // ✗ No comments
undefined: '{"value": undefined}', // ✗ Use null instead
NaN: '{"value": NaN}', // ✗ Use null or string
function: '{"fn": function() {}}', // ✗ No functions
unescaped: '{"text": "Hello "World""}', // ✗ Must escape quotes
multilineString: '{"text": "Line 1\nLine 2"}', // ✗ Must use \\n
};
console.log('⚠️ JSON is strict - one syntax error breaks everything');
Common JSON Mistakes
// The top 10 JSON syntax errors
const commonMistakes = [
{
rank: 1,
error: 'Trailing comma',
example: '{"a": 1, "b": 2,}',
fix: '{"a": 1, "b": 2}',
frequency: 'Very common',
why: 'JavaScript allows it, JSON does not'
},
{
rank: 2,
error: 'Single quotes',
example: "{'name': 'John'}",
fix: '{"name": "John"}',
frequency: 'Extremely common',
why: 'JavaScript allows both, JSON only allows double'
},
{
rank: 3,
error: 'Unquoted keys',
example: '{name: "John"}',
fix: '{"name": "John"}',
frequency: 'Common',
why: 'JavaScript object literals allow unquoted keys'
},
{
rank: 4,
error: 'Comments',
example: '{"name": "John" /* comment */}',
fix: '{"name": "John"}',
frequency: 'Common',
why: 'JSON spec does not support comments'
},
{
rank: 5,
error: 'Missing comma',
example: '{"a": 1 "b": 2}',
fix: '{"a": 1, "b": 2}',
frequency: 'Very common',
why: 'Easy to miss when editing'
},
{
rank: 6,
error: 'Unescaped quotes',
example: '{"text": "Hello "World""}',
fix: '{"text": "Hello \\"World\\""}',
frequency: 'Common',
why: 'Forget to escape inner quotes'
},
{
rank: 7,
error: 'NaN or Infinity',
example: '{"value": NaN}',
fix: '{"value": null}',
frequency: 'Common in JavaScript serialization',
why: 'JavaScript has these values, JSON does not'
},
{
rank: 8,
error: 'Undefined',
example: '{"value": undefined}',
fix: '{"value": null}',
frequency: 'Common',
why: 'JavaScript has undefined, JSON does not'
},
{
rank: 9,
error: 'Multiline strings',
example: '{"text": "Line 1\nLine 2"}',
fix: '{"text": "Line 1\\nLine 2"}',
frequency: 'Common',
why: 'Actual newlines must be escaped'
},
{
rank: 10,
error: 'Duplicate keys',
example: '{"a": 1, "a": 2}',
fix: '{"a": 2}',
frequency: 'Occasional',
why: 'Valid JSON but ambiguous, use unique keys'
}
];
console.log('Top 10 JSON mistakes - Learn these to save hours!');
Implementation Methods
1. JavaScript JSON Formatter & Validator
// Production-ready JSON formatter and validator
class JSONFormatter {
// Validate JSON
static validate(jsonString) {
const errors = [];
const warnings = [];
try {
// Check for common issues before parsing
this.preValidate(jsonString, errors, warnings);
// Parse JSON
const parsed = JSON.parse(jsonString);
// Post-parse validation
this.postValidate(parsed, warnings);
return {
valid: errors.length === 0,
parsed: errors.length === 0 ? parsed : null,
errors,
warnings
};
} catch (error) {
errors.push({
message: error.message,
position: this.findErrorPosition(jsonString, error)
});
return { valid: false, parsed: null, errors, warnings };
}
}
// Pre-parse validation checks
static preValidate(jsonString, errors, warnings) {
// Check for trailing commas
if (/,\s*[}\]]/.test(jsonString)) {
warnings.push('Trailing comma detected (may fail in strict parsers)');
}
// Check for single quotes
if (/'[^']*'/.test(jsonString)) {
errors.push('Single quotes detected - JSON requires double quotes');
}
// Check for comments
if (/\/\/|\/\*/.test(jsonString)) {
warnings.push('Comments detected - not valid in JSON spec');
}
// Check for undefined
if (/undefined/.test(jsonString)) {
errors.push('undefined detected - use null instead');
}
// Check for NaN
if (/\bNaN\b/.test(jsonString)) {
errors.push('NaN detected - use null or string instead');
}
// Check for Infinity
if (/\bInfinity\b/.test(jsonString)) {
errors.push('Infinity detected - use null or large number instead');
}
}
// Post-parse validation checks
static postValidate(parsed, warnings) {
// Check for duplicate keys in objects
const checkDuplicates = (obj, path = '') => {
if (typeof obj !== 'object' || obj === null) return;
if (Array.isArray(obj)) {
obj.forEach((item, i) => checkDuplicates(item, `${path}[${i}]`));
} else {
const keys = Object.keys(obj);
const uniqueKeys = new Set(keys);
if (keys.length !== uniqueKeys.size) {
warnings.push(`Duplicate keys detected at ${path || 'root'}`);
}
keys.forEach(key => checkDuplicates(obj[key], path ? `${path}.${key}` : key));
}
};
checkDuplicates(parsed);
}
// Find error position in JSON string
static findErrorPosition(jsonString, error) {
const match = error.message.match(/position (\d+)/);
if (match) {
const pos = parseInt(match[1]);
const lines = jsonString.substring(0, pos).split('\n');
return {
line: lines.length,
column: lines[lines.length - 1].length + 1,
position: pos
};
}
return null;
}
// Format JSON with options
static format(jsonString, options = {}) {
const {
indent = 2,
sortKeys = false,
trailingComma = false
} = options;
try {
let parsed = JSON.parse(jsonString);
if (sortKeys) {
parsed = this.sortObjectKeys(parsed);
}
let formatted = JSON.stringify(parsed, null, indent);
if (trailingComma) {
// Add trailing commas (non-standard)
formatted = formatted.replace(/([}\]])/g, ',$1');
}
return { success: true, formatted, error: null };
} catch (error) {
return { success: false, formatted: null, error: error.message };
}
}
// Sort object keys recursively
static sortObjectKeys(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
if (Array.isArray(obj)) {
return obj.map(item => this.sortObjectKeys(item));
}
const sorted = {};
Object.keys(obj).sort().forEach(key => {
sorted[key] = this.sortObjectKeys(obj[key]);
});
return sorted;
}
// Minify JSON
static minify(jsonString) {
try {
const parsed = JSON.parse(jsonString);
return { success: true, minified: JSON.stringify(parsed), error: null };
} catch (error) {
return { success: false, minified: null, error: error.message };
}
}
// Pretty print with colors (for terminal)
static prettyPrint(jsonString) {
try {
const parsed = JSON.parse(jsonString);
const formatted = JSON.stringify(parsed, null, 2);
console.log('\n' + formatted + '\n');
return { success: true };
} catch (error) {
console.error('Invalid JSON:', error.message);
return { success: false, error: error.message };
}
}
// Compare two JSON objects
static compare(json1, json2) {
try {
const obj1 = typeof json1 === 'string' ? JSON.parse(json1) : json1;
const obj2 = typeof json2 === 'string' ? JSON.parse(json2) : json2;
const differences = this.findDifferences(obj1, obj2);
return {
equal: differences.length === 0,
differences
};
} catch (error) {
return { equal: false, error: error.message };
}
}
// Find differences between objects
static findDifferences(obj1, obj2, path = '') {
const diffs = [];
if (typeof obj1 !== typeof obj2) {
diffs.push({
path,
type: 'type_mismatch',
value1: typeof obj1,
value2: typeof obj2
});
return diffs;
}
if (typeof obj1 !== 'object' || obj1 === null) {
if (obj1 !== obj2) {
diffs.push({ path, type: 'value_change', value1: obj1, value2: obj2 });
}
return diffs;
}
// Compare arrays
if (Array.isArray(obj1)) {
if (!Array.isArray(obj2)) {
diffs.push({ path, type: 'type_mismatch', value1: 'array', value2: 'object' });
return diffs;
}
if (obj1.length !== obj2.length) {
diffs.push({ path, type: 'length_change', value1: obj1.length, value2: obj2.length });
}
const maxLen = Math.max(obj1.length, obj2.length);
for (let i = 0; i < maxLen; i++) {
diffs.push(...this.findDifferences(obj1[i], obj2[i], `${path}[${i}]`));
}
return diffs;
}
// Compare objects
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
const allKeys = new Set([...keys1, ...keys2]);
allKeys.forEach(key => {
const fullPath = path ? `${path}.${key}` : key;
if (!(key in obj1)) {
diffs.push({ path: fullPath, type: 'added', value: obj2[key] });
} else if (!(key in obj2)) {
diffs.push({ path: fullPath, type: 'removed', value: obj1[key] });
} else {
diffs.push(...this.findDifferences(obj1[key], obj2[key], fullPath));
}
});
return diffs;
}
}
// Usage examples
const jsonString = '{"name": "John", "age": 30, "city": "New York"}';
// Validate
const validation = JSONFormatter.validate(jsonString);
console.log('Valid:', validation.valid);
console.log('Errors:', validation.errors);
console.log('Warnings:', validation.warnings);
// Format
const formatted = JSONFormatter.format(jsonString, { indent: 2, sortKeys: true });
console.log('Formatted:', formatted.formatted);
// Minify
const minified = JSONFormatter.minify(jsonString);
console.log('Minified:', minified.minified);
// Compare
const json1 = '{"a": 1, "b": 2}';
const json2 = '{"a": 1, "b": 3, "c": 4}';
const comparison = JSONFormatter.compare(json1, json2);
console.log('Equal:', comparison.equal);
console.log('Differences:', comparison.differences);
2. Express API for JSON Operations
const express = require('express');
const app = express();
app.use(express.json({ limit: '10mb' }));
app.use(express.text({ type: 'application/json', limit: '10mb' }));
// Validate JSON
app.post('/api/json/validate', (req, res) => {
try {
const jsonString = typeof req.body === 'string' ? req.body : JSON.stringify(req.body);
const result = JSONFormatter.validate(jsonString);
res.json(result);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Format JSON
app.post('/api/json/format', (req, res) => {
try {
const { json, indent = 2, sortKeys = false } = req.body;
const result = JSONFormatter.format(json, { indent, sortKeys });
if (result.success) {
res.json({ formatted: result.formatted });
} else {
res.status(400).json({ error: result.error });
}
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Minify JSON
app.post('/api/json/minify', (req, res) => {
try {
const { json } = req.body;
const result = JSONFormatter.minify(json);
if (result.success) {
res.json({
minified: result.minified,
originalSize: json.length,
minifiedSize: result.minified.length,
savings: ((1 - result.minified.length / json.length) * 100).toFixed(2) + '%'
});
} else {
res.status(400).json({ error: result.error });
}
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Compare JSON
app.post('/api/json/compare', (req, res) => {
try {
const { json1, json2 } = req.body;
const result = JSONFormatter.compare(json1, json2);
res.json(result);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// JSON to CSV
app.post('/api/json/to-csv', (req, res) => {
try {
const { json } = req.body;
const data = typeof json === 'string' ? JSON.parse(json) : json;
if (!Array.isArray(data)) {
return res.status(400).json({ error: 'JSON must be an array for CSV conversion' });
}
if (data.length === 0) {
return res.json({ csv: '' });
}
// Get all unique keys
const keys = [...new Set(data.flatMap(obj => Object.keys(obj)))];
// Create CSV
const csv = [
keys.join(','),
...data.map(obj => keys.map(key => {
const value = obj[key];
const str = value === null || value === undefined ? '' : String(value);
return str.includes(',') ? `"${str}"` : str;
}).join(','))
].join('\n');
res.json({ csv });
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// JSON Schema validation
app.post('/api/json/validate-schema', (req, res) => {
try {
const { json, schema } = req.body;
const data = typeof json === 'string' ? JSON.parse(json) : json;
const schemaObj = typeof schema === 'string' ? JSON.parse(schema) : schema;
// Basic schema validation (use ajv for production)
const errors = validateSchema(data, schemaObj);
res.json({
valid: errors.length === 0,
errors
});
} catch (error) {
res.status(400).json({ error: error.message });
}
});
function validateSchema(data, schema) {
const errors = [];
if (schema.type && typeof data !== schema.type) {
errors.push(`Expected type ${schema.type}, got ${typeof data}`);
}
if (schema.required && typeof data === 'object') {
schema.required.forEach(field => {
if (!(field in data)) {
errors.push(`Missing required field: ${field}`);
}
});
}
return errors;
}
app.listen(3000, () => {
console.log('JSON API running on port 3000');
console.log('POST /api/json/validate - Validate JSON');
console.log('POST /api/json/format - Format JSON');
console.log('POST /api/json/minify - Minify JSON');
console.log('POST /api/json/compare - Compare JSON');
console.log('POST /api/json/to-csv - Convert to CSV');
console.log('POST /api/json/validate-schema - Schema validation');
});
3. Python Implementation
import json
import re
from typing import Dict, Any, List
class JSONFormatter:
@staticmethod
def validate(json_string: str) -> Dict[str, Any]:
"""Validate JSON string"""
errors = []
warnings = []
try:
# Pre-validation checks
JSONFormatter._pre_validate(json_string, errors, warnings)
# Parse JSON
parsed = json.loads(json_string)
return {
'valid': len(errors) == 0,
'parsed': parsed if len(errors) == 0 else None,
'errors': errors,
'warnings': warnings
}
except json.JSONDecodeError as e:
errors.append({
'message': str(e),
'line': e.lineno,
'column': e.colno
})
return {
'valid': False,
'parsed': None,
'errors': errors,
'warnings': warnings
}
@staticmethod
def _pre_validate(json_string: str, errors: List, warnings: List):
"""Pre-parse validation checks"""
if re.search(r',\s*[}\]]', json_string):
warnings.append('Trailing comma detected')
if re.search(r"'[^']*'", json_string):
errors.append('Single quotes detected - use double quotes')
if 'undefined' in json_string:
errors.append('undefined detected - use null instead')
@staticmethod
def format(json_string: str, indent: int = 2, sort_keys: bool = False) -> Dict[str, Any]:
"""Format JSON with options"""
try:
parsed = json.loads(json_string)
formatted = json.dumps(parsed, indent=indent, sort_keys=sort_keys)
return {'success': True, 'formatted': formatted, 'error': None}
except Exception as e:
return {'success': False, 'formatted': None, 'error': str(e)}
@staticmethod
def minify(json_string: str) -> Dict[str, Any]:
"""Minify JSON"""
try:
parsed = json.loads(json_string)
minified = json.dumps(parsed, separators=(',', ':'))
return {'success': True, 'minified': minified, 'error': None}
except Exception as e:
return {'success': False, 'minified': None, 'error': str(e)}
# Usage
json_string = '{"name": "John", "age": 30}'
# Validate
result = JSONFormatter.validate(json_string)
print(f"Valid: {result['valid']}")
# Format
formatted = JSONFormatter.format(json_string, indent=2, sort_keys=True)
print(f"Formatted: {formatted['formatted']}")
# Minify
minified = JSONFormatter.minify(json_string)
print(f"Minified: {minified['minified']}")
4. Quick Online Formatting & Validation
For rapid testing, debugging API responses, or fixing JSON syntax errors, using a JSON formatter & validator can instantly format and validate without writing code. This is particularly useful when:
- Debugging APIs: Beautify compact JSON responses
- Config files: Validate syntax before deployment
- Data inspection: Format minified JSON for readability
- Error fixing: Find and fix syntax errors quickly
For production applications, integrate JSON validation into your CI/CD pipeline and API endpoints to catch errors before they cause issues.
Real-World Use Cases
1. API Response Debugging
// Debug API responses
class APIDebugger {
static async debugRequest(url) {
console.log(`\n=== Debugging API: ${url} ===\n`);
try {
const response = await fetch(url);
const text = await response.text();
console.log('Raw response:');
console.log(text.substring(0, 200) + '...\n');
// Validate JSON
const validation = JSONFormatter.validate(text);
if (!validation.valid) {
console.log('❌ Invalid JSON!');
console.log('Errors:', validation.errors);
console.log('Warnings:', validation.warnings);
return;
}
console.log('✓ Valid JSON\n');
// Format for readability
const formatted = JSONFormatter.format(text, { indent: 2 });
console.log('Formatted response:');
console.log(formatted.formatted);
} catch (error) {
console.error('Request failed:', error.message);
}
}
}
// Usage
await APIDebugger.debugRequest('https://api.example.com/users/1');
2. Config File Validation
// Validate config files in CI/CD
const fs = require('fs').promises;
async function validateConfigFiles() {
const configFiles = [
'config/development.json',
'config/production.json',
'config/test.json'
];
let allValid = true;
for (const file of configFiles) {
console.log(`\nValidating: ${file}`);
try {
const content = await fs.readFile(file, 'utf8');
const validation = JSONFormatter.validate(content);
if (validation.valid) {
console.log('✓ Valid');
} else {
console.log('❌ Invalid');
validation.errors.forEach(err => console.log(' -', err.message));
allValid = false;
}
if (validation.warnings.length > 0) {
console.log('Warnings:');
validation.warnings.forEach(warn => console.log(' -', warn));
}
} catch (error) {
console.log('❌ Failed to read file:', error.message);
allValid = false;
}
}
if (!allValid) {
process.exit(1); // Fail CI/CD pipeline
}
console.log('\n✓ All config files valid');
}
// Run in CI/CD
validateConfigFiles();
3. JSON Schema Validation
// Validate data against schema
const Ajv = require('ajv');
const ajv = new Ajv();
class SchemaValidator {
static validate(data, schema) {
const validate = ajv.compile(schema);
const valid = validate(data);
return {
valid,
errors: validate.errors || []
};
}
static example() {
const schema = {
type: 'object',
required: ['name', 'email', 'age'],
properties: {
name: { type: 'string', minLength: 1 },
email: { type: 'string', format: 'email' },
age: { type: 'number', minimum: 0, maximum: 150 }
}
};
const validData = {
name: 'John Doe',
email: 'john@example.com',
age: 30
};
const invalidData = {
name: '',
email: 'invalid-email',
age: -5
};
console.log('\nValid data:');
console.log(this.validate(validData, schema));
console.log('\nInvalid data:');
console.log(this.validate(invalidData, schema));
}
}
SchemaValidator.example();
4. JSON Diff Tool
// Compare API responses
class JSONDiff {
static diff(json1, json2) {
const comparison = JSONFormatter.compare(json1, json2);
console.log('\n=== JSON Comparison ===\n');
if (comparison.equal) {
console.log('✓ JSON objects are identical\n');
return;
}
console.log('❌ JSON objects differ:\n');
comparison.differences.forEach(diff => {
console.log(`Path: ${diff.path}`);
console.log(`Type: ${diff.type}`);
if (diff.value1 !== undefined) console.log(`Value 1: ${JSON.stringify(diff.value1)}`);
if (diff.value2 !== undefined) console.log(`Value 2: ${JSON.stringify(diff.value2)}`);
if (diff.value !== undefined) console.log(`Value: ${JSON.stringify(diff.value)}`);
console.log('');
});
}
}
// Usage - compare API versions
const v1Response = '{"name": "John", "age": 30}';
const v2Response = '{"name": "John", "age": 31, "email": "john@example.com"}';
JSONDiff.diff(v1Response, v2Response);
Testing JSON Operations
// Jest tests
describe('JSON Formatter & Validator', () => {
test('validates correct JSON', () => {
const json = '{"name": "John", "age": 30}';
const result = JSONFormatter.validate(json);
expect(result.valid).toBe(true);
expect(result.errors).toHaveLength(0);
});
test('detects trailing comma', () => {
const json = '{"name": "John",}';
const result = JSONFormatter.validate(json);
expect(result.warnings.length).toBeGreaterThan(0);
});
test('detects single quotes', () => {
const json = "{'name': 'John'}";
const result = JSONFormatter.validate(json);
expect(result.valid).toBe(false);
expect(result.errors.some(e => e.message.includes('single quotes'))).toBe(true);
});
test('formats JSON correctly', () => {
const json = '{"name":"John","age":30}';
const result = JSONFormatter.format(json, { indent: 2 });
expect(result.success).toBe(true);
expect(result.formatted).toContain('\n');
});
test('minifies JSON', () => {
const json = '{\n "name": "John",\n "age": 30\n}';
const result = JSONFormatter.minify(json);
expect(result.success).toBe(true);
expect(result.minified.length).toBeLessThan(json.length);
expect(result.minified).not.toContain('\n');
});
test('compares JSON objects', () => {
const json1 = '{"a": 1, "b": 2}';
const json2 = '{"a": 1, "b": 3}';
const result = JSONFormatter.compare(json1, json2);
expect(result.equal).toBe(false);
expect(result.differences.length).toBeGreaterThan(0);
});
});
Conclusion: Perfect JSON Every Time
JSON formatting and validation are essential skills for modern development. From debugging API responses to validating config files, proper JSON handling prevents bugs, saves debugging time, and ensures reliable data interchange.
✅ Zero syntax errors (catch before deployment)
✅ Instant debugging (find errors in seconds)
✅ Professional APIs (well-formatted responses)
✅ Config validation (prevent deployment failures)
✅ Data integrity (valid JSON always parses)
✅ Team collaboration (consistent formatting)
✅ Time savings (no more syntax hunting)
✅ CI/CD integration (automated validation)
JSON Best Practices:
✓ Always validate JSON before deployment
✓ Use consistent formatting (2 or 4 space indent)
✓ Avoid trailing commas (not in JSON spec)
✓ Use double quotes only (never single)
✓ Quote all object keys
✓ Use null instead of undefined
✓ Validate against schemas for APIs
✓ Minify JSON for production (save bandwidth)
✗ Never commit unformatted JSON
✗ Never skip validation in CI/CD
✗ Never use comments in JSON files
The Bottom Line:
JSON is strict. One missing comma, one single quote, one trailing comma can break your entire application. Validation catches these errors instantly—before they cause production incidents, before they waste hours of debugging, before they cost money. Format your JSON. Validate your JSON. Deploy confidently.
What's your worst JSON debugging story? Share in the comments!
Top comments (0)