JSON — JavaScript Object Notation — has become the dominant data interchange format on the web. From REST APIs to configuration files, from webhooks to local storage, JSON is everywhere. Despite its apparent simplicity, working with JSON professionally requires understanding its specification precisely, knowing how to validate and format it efficiently, and recognising the failure modes that silently corrupt data or crash integrations. This guide covers every aspect of JSON that matters in production web development.
The JSON Specification
JSON is defined by ECMA-404 and RFC 8259. The specification is notably brief — the entire grammar fits on two pages — but its precision has several non-obvious consequences. JSON supports exactly six value types: string, number, boolean (true/false), null, array, and object. Dates are not a JSON type — they are typically represented as ISO 8601 strings or Unix timestamps. Undefined is not a JSON value — a JavaScript object with undefined-valued keys will silently drop those keys during JSON.stringify(). Functions, regular expressions, and symbols are also not representable in JSON.
JSON strings must be enclosed in double quotes. Single-quoted strings are not valid JSON, even though JavaScript accepts them as string literals. This is one of the most common causes of JSON parse failures when developers write JSON by hand or when templating systems interpolate values with single-quoted strings. Always use a JSON serialiser rather than hand-constructing JSON strings.
JSON numbers follow the IEEE 754 double-precision floating-point specification with no NaN or Infinity values. This has a critical implication: integers larger than 2^53 − 1 (9,007,199,254,740,991) cannot be represented exactly in JSON without losing precision. Database primary keys, Twitter IDs, and any system using 64-bit integers must transmit large integers as strings to prevent silent data corruption. This is why many APIs return IDs in both a numeric field and a string field — the numeric field exists for legacy clients, and the string field is the safe version for all modern implementations.
Formatting and Indentation
JSON can be formatted two ways: minified (all whitespace removed) or pretty-printed (with consistent indentation). Minified JSON is ideal for network transmission — removing whitespace can reduce payload size by 20–40% for typical API responses. Pretty-printed JSON is readable by humans and essential for configuration files, version-controlled data, and debugging sessions.
The standard indentation size for pretty-printed JSON is 2 spaces, though 4 spaces is also common. JSON does not prescribe indentation size — any consistent whitespace is valid. When committing JSON configuration files to version control, establish a team convention (typically 2 spaces) and enforce it with a linter to prevent noisy diffs caused by indentation changes.
Key ordering in JSON objects is not specified — the standard does not guarantee that keys will appear in any particular order when serialised or parsed. This means two semantically identical JSON objects may produce different byte-level representations, which breaks naive diff comparisons and can cause incorrect cache invalidation. If key ordering matters for your use case — canonical JSON for cryptographic signing, deterministic configuration diffs — you need a JSON canonicalisation step that sorts keys alphabetically before serialisation.
Our JSON Formatter tool supports pretty-printing with configurable indentation, minification, and real-time validation. Paste any JSON — including API responses, configuration blobs, or log events — and the tool instantly validates it, highlights syntax errors with exact line numbers, and offers both a formatted and minified version for copying.
Validation and Error Diagnosis
JSON parse errors follow predictable patterns that, once recognised, are quick to fix. The most common category is trailing commas — adding a comma after the last element of an array or the last key-value pair of an object is invalid JSON, though it is valid JavaScript. This catches many developers who write JSON as if it were JS object literal syntax.
Missing or unmatched brackets are the second most common error type. A deep JSON object with many nested levels can have a bracket mismatch that a human eye struggles to locate. A proper validator counts opening and closing brackets and braces, identifies the exact line where the mismatch occurs, and highlights the location. Trying to find a missing bracket by reading the raw text is several times slower.
Control characters embedded in string values cause subtle parse failures. If a JSON string contains a raw newline (U+000A) or carriage return (U+000D) without escaping them as \n or \r, the JSON is invalid even though the characters are present. This typically happens when JSON is constructed by string interpolation rather than by a serialiser. The fix is always to use a proper JSON serialiser — JSON.stringify() in JavaScript, json.dumps() in Python, etc.
Unicode escape sequences in JSON must be exactly four hexadecimal digits: \u0041 is valid, \u41 is not. Surrogate pairs for characters outside the Basic Multilingual Plane (U+10000 and above) require two consecutive Unicode escapes: a high surrogate (\uD800–\uDBFF) followed by a low surrogate (\uDC00–\uDFFF). Incorrect surrogate handling causes emoji and other supplementary characters to appear corrupted in JSON that passes basic syntax validation.
JSON in HTTP APIs
JSON is the default body format for HTTP REST APIs. When sending JSON, always include the Content-Type: application/json header. When expecting JSON in a response, include Accept: application/json. Missing or incorrect Content-Type headers cause silent failures where the server either rejects the body or parses it as a different format.
Response body size is a frequently neglected API design concern. Sending large JSON payloads for operations that only need a small subset of the data wastes bandwidth, increases latency, and raises client memory requirements. REST APIs should support field selection (via a fields query parameter) or use GraphQL if field-level querying is a common pattern. Even without formal field selection, reviewing your API's response envelopes and pruning unused fields is a high-value, low-cost optimisation.
Error responses should also use JSON with a consistent structure. A widely adopted convention: {"error": {"code": "VALIDATION_FAILED", "message": "human-readable description", "details": [...]}}. Consistent error envelopes enable client-side error handling code to be generic rather than endpoint-specific. Document your error schema as part of your API specification.
JSON Schema for Validation
JSON Schema is a vocabulary for annotating and validating JSON documents. A schema defines the expected structure, types, and constraints for a JSON value: which keys are required, what types are acceptable, what ranges are valid for numbers, what patterns are valid for strings. Validating incoming API requests against a JSON Schema at the boundary layer — before the data reaches your business logic — catches a huge category of integration errors early and with clear error messages.
Schema-driven development also enables automatic documentation generation, mock server creation, and contract testing. Tools like OpenAPI (which embeds JSON Schema) produce interactive API documentation from schema definitions. TypeScript projects can generate TypeScript types from JSON Schemas, providing compile-time type safety for API response handling. This entire ecosystem starts with a well-defined schema.
Writing JSON Schemas by hand can be tedious for complex objects. Tools that infer a schema from sample JSON data — inferring types, detecting required fields, identifying common patterns — provide a good starting point that you refine rather than authoring from scratch. Always review auto-generated schemas carefully; they reflect what the sample data contained, not necessarily all valid variations that your actual API might produce.
JSON Performance Considerations
JSON parsing is synchronous and CPU-bound in JavaScript. Parsing a 10MB JSON response on the main browser thread will block rendering for a perceptible duration. For large payloads, stream-based parsing (using the Response.body readable stream with a JSON streaming library) or offloading parsing to a Web Worker prevents jank. In Node.js, similarly large payloads should be parsed in worker threads rather than the event loop thread.
Repeated JSON.parse() / JSON.stringify() calls for deep-cloning objects is a common performance anti-pattern. While structurally simple and correct for serialisable objects, it is slower than using structured clone (structuredClone() in modern environments) for objects that don't need to survive serialisation. Profile before optimising, but be aware that JSON round-trips have a measurable cost.
NDJSON (Newline-Delimited JSON) is a format where each line is a self-contained JSON document. It's ideal for log files, streaming APIs, and large dataset exports because it enables line-by-line processing without parsing the entire document first. If you're building a log pipeline, a data export feature, or a streaming API endpoint, NDJSON is worth understanding as a more practical alternative to a single massive JSON array.
Security: JSON Injection and Prototype Pollution
JSON injection occurs when user-supplied data is interpolated into a JSON string without sanitisation, allowing an attacker to inject valid JSON that alters the document structure. The fix is absolute: always use a JSON serialiser to produce JSON. Never construct JSON strings through template literals or string concatenation with user data.
Prototype pollution is a JavaScript-specific vulnerability where an attacker crafts a JSON payload containing the key __proto__. When this JSON is parsed and assigned to a JavaScript object using a naive deep merge utility, it modifies Object.prototype — affecting every object in the application. Use a JSON parsing library or deep merge utility that explicitly checks for and rejects __proto__, constructor, and prototype keys. Modern Node.js versions include prototype pollution protections in JSON.parse(), but third-party merge utilities may still be vulnerable.