JSON (JavaScript Object Notation) has become the universal data interchange format of the web. Over 90% of modern REST APIs use JSON for request and response bodies. Configuration files, database records, log entries, and inter-service communication all rely on properly formatted JSON. Yet formatting errors remain one of the most common causes of API failures and debugging headaches. This guide covers everything developers need to know about JSON formatting: syntax rules, common mistakes, validation strategies, performance considerations, and the best tools for working with JSON efficiently.
JSON supports exactly six data types: strings, numbers, booleans (true/false), null, objects, and arrays. Understanding these types and their formatting rules is the foundation of working with JSON correctly.
Strings must be enclosed in double quotes. Single quotes are not valid JSON. Strings support escape sequences: \" for a literal double quote, \\ for a backslash, \n for a newline, \t for a tab, and \uXXXX for Unicode characters. Every control character (U+0000 through U+001F) must be escaped.
Numbers can be integers or floating point. They may be negative (prefixed with a minus sign). Scientific notation is supported: 2.5e10 is valid JSON. However, leading zeros are not allowed (write 0.5 not .5), and special values like NaN, Infinity, and -Infinity are not valid JSON numbers.
Objects are unordered collections of key-value pairs enclosed in curly braces. Every key must be a double-quoted string. Key-value pairs are separated by colons, and pairs are separated by commas:
{
"name": "Zovo Tools",
"version": 2,
"isActive": true,
"metadata": null,
"features": ["formatting", "validation", "minification"]
}
Arrays are ordered lists of values enclosed in square brackets. Values are separated by commas. Arrays can contain mixed types, though this is generally discouraged for API design clarity.
Debugging invalid JSON is a routine part of development. Here are the errors that account for roughly 90% of JSON parsing failures, ranked by frequency:
The single most common JSON error. Unlike JavaScript, Python dictionaries, and most programming languages, JSON does not allow a comma after the last item in an object or array:
// INVALID - trailing comma
{
"name": "test",
"value": 42,
}
// VALID
{
"name": "test",
"value": 42
}
JSON requires double quotes exclusively. This trips up Python developers who often use single quotes for strings:
// INVALID
{'name': 'test'}
// VALID
{"name": "test"}
JavaScript allows unquoted object keys, but JSON does not:
// INVALID
{name: "test"}
// VALID
{"name": "test"}
JSON has no comment syntax. Neither // nor /* */ are valid. Douglas Crockford, the creator of JSON, intentionally excluded comments to prevent their misuse as parsing directives. If you need comments in configuration files, consider JSON5 or JSONC formats.
Forgetting a comma between items produces cryptic parse errors. The error message typically points to the line after the missing comma, not the line where the comma should be:
// INVALID - missing comma between "name" and "value"
{
"name": "test"
"value": 42
}
Every mainstream programming language provides built-in JSON formatting capabilities. Here is how to pretty-print JSON in the languages you are most likely to encounter:
// Pretty print with 2-space indentation
const formatted = JSON.stringify(data, null, 2);
// Minify (remove all whitespace)
const minified = JSON.stringify(data);
// Custom replacer function
const filtered = JSON.stringify(data, (key, value) => {
if (key === 'password') return undefined;
return value;
}, 2);
import json
# Pretty print with 2-space indentation
formatted = json.dumps(data, indent=2)
# Sort keys alphabetically
sorted_json = json.dumps(data, indent=2, sort_keys=True)
# Minify with compact separators
minified = json.dumps(data, separators=(',', ':'))
# Handle non-ASCII characters
safe_json = json.dumps(data, ensure_ascii=False)
import "encoding/json"
// Pretty print
formatted, err := json.MarshalIndent(data, "", " ")
// Minify
minified, err := json.Marshal(data)
# Pretty print a JSON file
cat data.json | jq .
# Minify
cat data.json | jq -c .
# Extract specific fields
cat data.json | jq '.users[] | {name, email}'
# Sort keys
cat data.json | jq -S .
The jq command-line processor is arguably the most powerful JSON tool available. It supports filtering, mapping, grouping, and transforming JSON with a concise query syntax. For developers who work with JSON APIs frequently, learning jq pays enormous dividends.
Formatting ensures JSON is syntactically valid. Schema validation goes further by ensuring JSON conforms to a defined structure. JSON Schema (jsonschema.org) is the standard for describing JSON structure, types, and constraints.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": {
"type": "string",
"minLength": 1,
"maxLength": 100
},
"age": {
"type": "integer",
"minimum": 0,
"maximum": 150
},
"email": {
"type": "string",
"format": "email"
}
},
"required": ["name", "email"]
}
Schema validation catches structural errors that syntax validation misses: wrong data types, missing required fields, values outside acceptable ranges, and strings that do not match expected patterns. Integrating schema validation into your API layer prevents malformed data from reaching your business logic.
| Feature | JSON | XML | YAML | TOML |
|---|---|---|---|---|
| Human readability | Good | Moderate | Excellent | Excellent |
| Comments | No | Yes | Yes | Yes |
| Data types | 6 types | Text only | ~10 types | ~8 types |
| Parse speed | Fast | Slow | Moderate | Fast |
| File size | Small | Large | Smallest | Small |
| API usage | Dominant | Legacy | Rare | Rare |
| Config files | Common | Declining | Common | Growing |
| Schema support | JSON Schema | XSD/DTD | Limited | Limited |
JSON wins the API battle because of its native compatibility with JavaScript, lightweight syntax, and fast parsing. XML retains a presence in enterprise systems, SOAP APIs, and document formats. YAML dominates configuration files (Kubernetes, Docker Compose, GitHub Actions) thanks to its comment support and cleaner syntax. TOML is gaining ground for application configuration files, particularly in the Rust and Go ecosystems.
JSON minification removes all whitespace, newlines, and indentation without changing the data. For a typical API response, minification reduces payload size by 15-30%. Combined with gzip or Brotli compression, the savings compound dramatically.
Consider a 50KB formatted JSON API response. After minification, it might shrink to 38KB. After gzip compression, it drops to roughly 8KB. After Brotli compression (level 4), it reaches approximately 6.5KB. That is an 87% total reduction from the formatted original.
| Stage | Size | Reduction |
|---|---|---|
| Formatted JSON | 50 KB | Baseline |
| Minified | 38 KB | 24% |
| Minified + gzip | 8 KB | 84% |
| Minified + Brotli | 6.5 KB | 87% |
Always serve minified JSON from APIs. Pretty-printed JSON is for development, debugging, and documentation only. Modern web servers (Nginx, Apache, Cloudflare) handle gzip and Brotli compression transparently via configuration.
Standard JSON parsing loads the entire file into memory, which becomes problematic for files exceeding a few hundred megabytes. Three strategies address this limitation:
Streaming parsers read JSON token by token without loading the complete document. In Node.js, libraries like jsonstream and stream-json process arbitrarily large files with constant memory usage. In Python, ijson provides the same capability.
NDJSON (Newline Delimited JSON) stores one JSON object per line. Each line is an independent, valid JSON document. This format enables line-by-line processing, parallel parsing, and easy appending. Tools like jq support NDJSON natively with the --slurp flag.
Binary JSON formats like BSON (used by MongoDB), MessagePack, and CBOR offer faster serialization and deserialization than text-based JSON. MessagePack typically produces payloads 30-50% smaller than minified JSON while being 2-3x faster to parse. The trade-off is human readability.
JSON itself is a data format, not executable code. However, several security concerns arise from how applications process JSON:
JSON injection occurs when user-supplied data is concatenated into JSON strings without proper escaping. Always use your language's built-in JSON serializer rather than string concatenation. In JavaScript, JSON.stringify() handles all necessary escaping automatically.
Prototype pollution in JavaScript happens when parsed JSON contains keys like __proto__ or constructor that modify the object prototype chain. Use Object.create(null) for parsed data containers, or use libraries with prototype pollution protection.
Denial of service through deeply nested JSON is a real attack vector. A JSON document with 10,000 levels of nesting can crash parsers or cause stack overflows. Set maximum nesting depth limits in your parser configuration. Most production-grade parsers default to limits between 100 and 1000 levels.
| Feature | Chrome 122+ | Firefox 124+ | Safari 17+ | Edge 122+ |
|---|---|---|---|---|
| JSON.parse() | Full | Full | Full | Full |
| JSON.stringify() | Full | Full | Full | Full |
| structuredClone() | Full | Full | Full | Full |
| Response.json() | Full | Full | Full | Full |
| Import Assertions (JSON) | Full | Partial | Full | Full |
| JSON Modules | Full | Partial | Full | Full |
JSON Patch defines a format for describing changes to a JSON document. Instead of sending the entire updated object, you send only the differences:
[
{ "op": "replace", "path": "/name", "value": "New Name" },
{ "op": "add", "path": "/tags/-", "value": "new-tag" },
{ "op": "remove", "path": "/deprecated" }
]
This is particularly valuable for large documents where only a few fields change. The PATCH HTTP method combined with JSON Patch reduces bandwidth and simplifies conflict resolution in collaborative editing scenarios.
JSON Pointer provides a string syntax for identifying specific values within a JSON document. The path /users/0/name references the name of the first user in a users array. JSON Pointer is used within JSON Patch and JSON Schema for referencing document locations.
JSON Lines (JSONL) stores one JSON value per line, with newlines as delimiters. This format is ideal for log files, data streaming, and machine learning datasets because it supports append-only writes and line-by-line processing. Major platforms including BigQuery, Elasticsearch, and OpenAI's fine-tuning API accept JSONL input.
Well-designed JSON APIs follow consistent conventions that make them predictable and easy to consume. The naming convention debate (camelCase versus snake_case) has practical implications: JavaScript-heavy frontends prefer camelCase because it matches language conventions, while Python and Ruby backends often prefer snake_case. The most important rule is consistency within a single API. Google's JSON style guide and the JSON:API specification both provide comprehensive guidelines.
Pagination in JSON APIs typically uses one of three patterns. Offset pagination includes "offset" and "limit" parameters with a "total" count in the response. Cursor pagination uses an opaque "cursor" string that points to the next page, which performs better for large datasets because it does not require counting total records. Keyset pagination uses the last item's sort key as the starting point for the next page, combining cursor-level performance with offset-level predictability.
Error responses deserve as much attention as success responses. A well-structured JSON error should include a machine-readable error code, a human-readable message, and optionally a documentation URL and field-level validation details:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Request validation failed",
"details": [
{
"field": "email",
"message": "Must be a valid email address"
},
{
"field": "age",
"message": "Must be a positive integer"
}
],
"documentation": "https://api.example.com/docs/errors"
}
}
Envelope patterns (wrapping all responses in a "data" key) versus flat responses is another design decision. Envelopes provide a consistent top-level structure that accommodates metadata, pagination info, and error details alongside the primary data. Flat responses are simpler but require conventions for adding metadata. The JSON:API specification mandates an envelope with "data", "errors", "meta", and "links" top-level keys.
When JSON parsing fails, the error message is your primary diagnostic tool. Different parsers provide different levels of detail:
Chrome DevTools shows the exact character position where parsing failed when you use JSON.parse() in the console. The error message format is "Unexpected token X in JSON at position Y" where Y is the character offset from the start of the string. Count characters from the beginning of your JSON to locate the exact problem.
Node.js provides similar positional errors. For large JSON files, pipe through jq . to get color-coded error messages that show the line number and character position. The jq error messages are among the most helpful of any JSON parser.
Python's json.JSONDecodeError includes the line number, column number, and character position. When debugging, catch the exception and print its attributes: err.lineno, err.colno, and err.pos. For complex debugging scenarios, the json.tool command-line module (python -m json.tool input.json) validates and formats in one step.
A common debugging workflow for malformed JSON from external sources: first, try to parse it and note the error position. Then, use a JSON formatter tool like the Zovo JSON Formatter that provides visual error highlighting. Often the error cascades from an earlier issue (a missing comma on line 5 might cause a parse error on line 10), so look backward from the reported position for the root cause.
For JSON embedded in log files or mixed with other output, extract the JSON portion first. In bash, use grep to find lines containing opening braces, or use sed to extract content between the first { and last }. Then pipe the extracted content through jq . for validation. This extract-then-validate approach prevents false errors caused by surrounding non-JSON content.
JSON.stringify(data, null, 2) in JavaScript or json.dumps(data, indent=2) in Python.JSON.stringify(obj) without the space parameter. In Python, use json.dumps(obj, separators=(',', ':')). Command line: jq -c . input.json. Online tools like Zovo's JSON Formatter provide one-click minification.Update History:
Want a video tutorial? Search YouTube for step-by-step video guides on how to format json complete guide.