Skip to main content
CodeLint.Dev Dev Tools
Developer Tools 9 min read

The Complete Guide to JSON Formatting, Validation & Debugging

JSON is the lingua franca of modern APIs — but its deceptively simple syntax hides a surprisingly strict specification that trips up even experienced developers. This guide covers RFC 8259 in depth, the eight most common parse errors with exact fixes, code samples in six languages, and the edge cases that will catch you off guard in production.

Try the tool
JSON Formatter & Validator
Format & validate your JSON free →

What Is JSON? The RFC 8259 Specification

JSON (JavaScript Object Notation) is a lightweight, text-based data interchange format defined in RFC 8259 (December 2017), which superseded RFC 7159 and the original RFC 4627. Despite its name, JSON is entirely language-independent — it is supported natively or via a standard library in virtually every programming language on the planet.

RFC 8259 defines exactly six value types:

  • object — an unordered set of key/value pairs enclosed in {}
  • array — an ordered list of values enclosed in []
  • string — a Unicode character sequence enclosed in double quotes
  • number — an integer or floating-point value (finite only — no NaN, no Infinity)
  • boolean — the exact literals true or false
  • null — the exact literal null

RFC 8259 mandates UTF-8 encoding without a BOM for all JSON text transmitted over a network. UTF-16 and UTF-32 are technically permitted by the spec but rejected by the vast majority of parsers. In practice: always use UTF-8.

The specification is deliberately minimal — no comments, no trailing commas, no date literals, no binary types. This simplicity means a JSON document can be parsed by a deterministic finite automaton, which is why parsing is reliably fast even for megabyte-scale payloads.

JSON Syntax Rules Every Developer Must Know

The rules below are not conventions — they are hard requirements of the specification. Violating any of them produces invalid JSON that will cause a parse error in every standards-compliant parser.

  • Keys must be double-quoted strings. {"name": "Alice"} is valid. {name: "Alice"} and {'name': 'Alice'} are not.
  • No trailing commas. The last element in an array or the last key/value pair in an object must not be followed by a comma. This is the single most common JSON error.
  • No comments. Neither // line comments nor /* block comments */ are part of the JSON grammar. Use a separate documentation file or a superset like JSON5 if you need comments in config files.
  • Numbers have no leading zeros. 007 is not valid JSON. Use 7. The only exception is 0 itself, and numbers like 0.5.
  • Strings must escape certain characters. The backslash, double quote, and control characters U+0000 through U+001F must be escaped. The valid escape sequences are: \", \\, \/, \b, \f, \n, \r, \t, and \uXXXX.
  • undefined, NaN, and Infinity are JavaScript values, not JSON values. JSON.stringify(NaN) in JavaScript returns "null", which surprises many developers.
  • Object keys are technically unordered. The spec says implementations must not rely on key ordering. In practice, most modern parsers and engines preserve insertion order, but you cannot depend on it in a standard-compliant way.

The 8 Most Common JSON Errors — With Exact Fixes

The following errors account for the overwhelming majority of JSON parse failures in the wild. Each is shown with the invalid JSON, the error a typical parser reports, and the corrected version.

Error 1: Trailing comma

The most common error by far. JavaScript allows trailing commas in object and array literals; JSON does not.

// ❌ Invalid
{"name": "Alice", "age": 30,}

// ✅ Valid
{"name": "Alice", "age": 30}

Error 2: Single-quoted strings

JavaScript accepts single quotes for strings; JSON requires double quotes for both keys and values.

// ❌ Invalid
{'name': 'Alice'}

// ✅ Valid
{"name": "Alice"}

Error 3: Unquoted keys

// ❌ Invalid — valid JS object literal, invalid JSON
{name: "Alice", age: 30}

// ✅ Valid
{"name": "Alice", "age": 30}

Error 4: JavaScript-only values

// ❌ Invalid — undefined, NaN, and Infinity are not JSON
{"score": NaN, "rank": undefined, "ratio": Infinity}

// ✅ Valid — use null or omit the key
{"score": null, "ratio": null}

Error 5: Missing comma between elements

// ❌ Invalid
{"name": "Alice" "age": 30}
["one" "two" "three"]

// ✅ Valid
{"name": "Alice", "age": 30}
["one", "two", "three"]

Error 6: Comments in JSON

// ❌ Invalid — JSON has no comment syntax
{
  // User record
  "name": "Alice", /* admin user */
  "role": "admin"
}

// ✅ Remove comments, or use JSON5 / JSONC for config files
{"name": "Alice", "role": "admin"}

Error 7: Leading zeros in numbers

// ❌ Invalid — octal literal interpretation prevented by spec
{"zip": 07302, "code": 00124}

// ✅ Quote numeric codes that have meaningful leading zeros
{"zip": "07302", "code": "00124"}

Error 8: Unescaped control characters in strings

// ❌ Invalid — raw newline inside a JSON string
{"address": "123 Main St
Suite 400"}

// ✅ Use escape sequence
{"address": "123 Main St\nSuite 400"}

How to Format JSON in 6 Programming Languages

Every major language has a built-in or standard-library way to pretty-print JSON. The examples below format a compact JSON string with 2-space indentation — the most common convention for readability.

JavaScript JavaScript / Node.js
const raw = '{"name":"Alice","scores":[98,87,92]}';
const obj = JSON.parse(raw);
const pretty = JSON.stringify(obj, null, 2);
console.log(pretty);
/* Output:
{
  "name": "Alice",
  "scores": [98, 87, 92]
}
*/
Python Python
import json

raw = '{"name":"Alice","scores":[98,87,92]}'
obj = json.loads(raw)
pretty = json.dumps(obj, indent=2)
print(pretty)
# Output:
# {
#   "name": "Alice",
#   "scores": [98, 87, 92]
# }
Go Go
package main

import (
    "encoding/json"
    "fmt"
)

func main() {
    raw := []byte(`{"name":"Alice","scores":[98,87,92]}`)
    var obj map[string]interface{}
    json.Unmarshal(raw, &obj)
    pretty, _ := json.MarshalIndent(obj, "", "  ")
    fmt.Println(string(pretty))
}
Rust Rust (serde_json)
use serde_json::Value;

fn main() {
    let raw = r#"{"name":"Alice","scores":[98,87,92]}"#;
    let obj: Value = serde_json::from_str(raw).unwrap();
    let pretty = serde_json::to_string_pretty(&obj).unwrap();
    println!("{}", pretty);
}
Shell Command Line (jq)
# Pretty-print with jq (install: brew install jq / apt install jq)
echo '{"name":"Alice","scores":[98,87,92]}' | jq .

# From a file
cat data.json | jq .

# Sort keys + pretty-print
cat data.json | jq -S .

# Validate only (exit code 1 on error)
jq . data.json > /dev/null && echo "valid" || echo "invalid"
Java Java (Jackson)
import com.fasterxml.jackson.databind.ObjectMapper;

public class FormatJson {
    public static void main(String[] args) throws Exception {
        String raw = "{\"name\":\"Alice\",\"scores\":[98,87,92]}";
        ObjectMapper mapper = new ObjectMapper();
        Object obj = mapper.readValue(raw, Object.class);
        String pretty = mapper
            .writerWithDefaultPrettyPrinter()
            .writeValueAsString(obj);
        System.out.println(pretty);
    }
}

JSON Edge Cases That Catch Developers Off Guard

Large integer precision loss

JSON numbers are specified as arbitrary-precision, but JavaScript's JSON.parse() parses all numbers as IEEE 754 doubles. Any integer larger than Number.MAX_SAFE_INTEGER (253 − 1 = 9,007,199,254,740,991) will lose precision silently:

JSON.parse('{"id": 9007199254740993}')
// → { id: 9007199254740992 }  ← wrong! lost 1

// Fix: receive large integers as strings
JSON.parse('{"id": "9007199254740993"}')
// → { id: "9007199254740993" }  ← correct

This is a real-world problem with Twitter/X snowflake IDs (which exceed safe integer range). The Twitter API ships IDs as both a number field and a string field (id and id_str) specifically because of this limitation.

Duplicate keys — technically allowed, practically dangerous

RFC 8259 explicitly says objects with duplicate keys produce undefined behaviour. Most parsers silently take the last value, but some take the first, and some throw. Never produce JSON with duplicate keys.

Unicode and surrogate pairs

JSON is Unicode, but if your source data contains surrogate pairs (characters above U+FFFF encoded as two UTF-16 code units), some parsers will reject them unless they are correctly paired. \uD800 through \uDFFF are not valid standalone Unicode scalar values in JSON. Use a formatter or validator to surface these before they reach production.

The empty document

RFC 8259 says a valid JSON text is any single JSON value — not just an object or array. "hello", 42, true, and null are all complete, valid JSON documents. Many developers assume JSON must start with { or [; this is a common misconception.

Format vs Minify: When to Use Each

Format (pretty-print) during development, debugging, code review, and whenever a human needs to read the JSON. Indented JSON is also essential when storing JSON in version control — diffs are readable and reviewable.

Minify in production for payloads transmitted over a network. A 4-space indented JSON object can be 30–60% larger than its minified equivalent. For high-traffic APIs serving millions of requests per day, that overhead adds up to real bandwidth costs and measurable latency on slow connections.

The practical workflow: keep a formatted copy for development and source control; run minification as a build step or in your API serialiser at runtime. Never commit minified JSON that humans need to review — always commit the formatted version.

Frequently Asked Questions

What is the difference between JSON Lint, Format, and Validate?
Lint reports every syntax error with line and column numbers. Format re-indents the JSON but does not change its data. Validate confirms the document strictly conforms to the RFC 8259 specification. The JSON Formatter on CodeLint.Dev runs all three simultaneously.
Why do trailing commas work in my JavaScript code but break JSON?
JavaScript object and array literals allow trailing commas as a syntactic convenience for cleaner diffs. JSON is a data format defined by its own specification (RFC 8259), not by JavaScript syntax rules. The two look similar but are not the same language.
Is there a maximum size for a JSON file?
RFC 8259 imposes no size limit. Practical limits are set by your parser's memory, the browser tab's memory, or server limits. Client-side formatters like this one are limited by the browser's JavaScript heap, but can handle files of several megabytes without issue.
Can JSON contain binary data?
Not natively. The standard approach is to Base64-encode binary data before embedding it in a JSON string, then decode it after parsing. This adds ~33% overhead in size. For binary-heavy APIs, consider alternative formats like MessagePack, CBOR, or Protocol Buffers.
What is the difference between JSON and JSON5?
JSON5 is a community extension of JSON that adds comments, trailing commas, single-quoted strings, unquoted keys, and hexadecimal numbers. It is popular for configuration files. It is not an IETF standard and is not supported by native JSON parsers — you need a dedicated JSON5 library.
Why does JSON.stringify in JavaScript turn NaN and Infinity into null?
Because NaN and Infinity are not representable in JSON. Rather than throwing an error, JavaScript's JSON.stringify silently substitutes null. If you need to preserve these values, encode them as strings: "NaN", "Infinity", "-Infinity".

Ready to try JSON Formatter & Validator?

Free, private, and runs entirely in your browser — no sign-up, no server, no data sent anywhere.

Open JSON Formatter & Validator