Skip to main content
Developer Tools

The Developer's JSON Guide: Everything Worth Knowing in 2026

The spec, the dialects that disagree, the numbers that quietly break, and the security advice that points at the wrong recursion. With the cluster map.

By 8 min read
Article title card. A large pair of amber curly braces, with the line: one format, three dialects

JSON has been the default interchange format for twenty years, and most of what goes wrong with it is not the format. It is the edges: which dialect a file is in, what happens to a 64-bit identifier, and which piece of code actually falls over on a hostile document.

This is the map. Each section points at the article that goes deeper.

A map of the JSON articles on this site: this guide at the centre, with five deeper articles covering dialects, tsconfig comments, debugging, Base64 and JWT verification
Five articles, five questions. This page is the index.

What JSON is

A subset of JavaScript object literal syntax, defined by RFC 8259, with a grammar that fits on a card1: objects with double-quoted string keys, arrays, double-quoted strings, decimal numbers, and true, false, null.

No comments. No trailing commas. No date type. No binary. That minimalism is why every language has a fast, consistent parser, and also why config files, APIs and binary payloads all need workarounds.

The dialects, and one correction

Three formats share the .json extension:

  • JSON, the strict grammar in RFC 8259.
  • JSONC, JSON with comments, used for VS Code's own configuration and for tsconfig.json.
  • JSON5, a much larger extension with its own published specification: unquoted keys, single quotes, hex numbers, NaN, trailing commas.

An earlier version of this guide said JSONC has "no trailing commas". That is the common summary and it is wrong. Microsoft's own documentation says the JSON with Comments mode accepts trailing commas while showing a warning2, and TypeScript reads a tsconfig.json containing them without a diagnostic. The jsonc-parser library, also Microsoft's, rejects them unless you pass allowTrailingComma.

So "is this valid JSONC" has no answer without naming the parser and its options, which is the real difference between JSONC and the other two: JSON has an RFC, JSON5 has a specification, and JSONC has defaults. JSON vs JSON5 vs JSONC works through what each parser actually accepts, and why tsconfig.json takes comments covers the specific case that sends most people looking.

When you have a file and do not know which of the three it is, paste it into the JSON Dialect Detector : it names the dialect, points at every construct strict JSON would reject, and converts the document down to plain JSON.

The rule that survives all of it: plain JSON for anything crossing a service boundary. JSONC where a specific tool natively supports it. JSON5 only when you have decided to trade portability for ergonomics, and said so in the filename.

The everyday operations

Formatting and validating. Our JSON Formatter parses and pretty-prints in the page. When the input is invalid it surfaces the engine's own error, which carries a character position for some failures and, for others, only a quoted snippet of the payload. That asymmetry catches people out and is worked through in Debugging Malformed JSON.

Getting it into a spreadsheet. JSON to CSV flattens an array of objects, nested keys in dot notation.

Getting it into a config format. JSON to YAML for Kubernetes, Actions and compose files. TOML to JSON for reading pyproject.toml or Cargo.toml from JavaScript tooling.

Binary, and the tokens built on it

JSON strings are Unicode text, so raw bytes cannot go in one. The standard workaround is Base64:

{ "filename": "avatar.png", "content": "iVBORw0KGgoAAAANSUhEUgAA..." }

Encoded, not encrypted. Anyone reading the JSON reads the bytes. Base64 Explained covers the variants, including the unpadded URL-safe form that JWTs use.

Which is what a JWT is: three Base64URL segments, the middle one a JSON object of claims. Our JWT Decoder shows you that payload without verifying the signature, which makes it a debugging tool and explicitly not a security one. The verification pitfalls, algorithm confusion foremost, are in JWT Authentication Mistakes.

Numbers, where the quiet bugs live

The specification allows arbitrary decimal numbers. Most parsers store every one of them as an IEEE 754 double3, and three consequences follow.

Integers stop being exact above 2^53, about 9 quadrillion. A 64-bit identifier past that silently changes value on the round trip, which is why the fix is to serialise identifiers as strings and never as numbers. Our JSON Formatter flags integers in that range when it sees them, which is a small thing that has saved real time.

Decimals are approximate. 0.1 + 0.2 serialises as 0.30000000000000004. Money belongs in strings or in integer minor units.

NaN and Infinity are not JSON, though some libraries emit them anyway. Python's json module does by default, in both directions, which is why such payloads round-trip inside Python and fail everywhere else.

Round-trip your largest identifier and your most precise decimal through the actual stack before shipping. It takes a minute and it is the only way to know.

Schemas

JSON Schema describes the shape of a JSON document in JSON, and is what most API validation is built on. OpenAPI wraps it for HTTP APIs and buys you generated clients.

For internal work, Zod or Pydantic get you the same validation with less ceremony. They are not interchange formats, so the moment an outside consumer needs to know your shape, you are back to JSON Schema.

Performance, measured rather than assumed

JSON.parse on Node 24 ran at about 434 MB/s on an 800 KB payload of typical API records. Fast enough that it is rarely your bottleneck, and slow enough to notice at sustained high throughput.

Compression is where the received wisdom is least useful. The same payload compressed 12 times smaller with gzip and 42 times with brotli, because five thousand records repeat the same key names over and over. A payload of mostly unique prose compresses far less. Quoting a fixed ratio is the error; the answer depends entirely on how repetitive your data is, and measuring your own takes one command.

Standard parsers also load the whole document into memory, so genuinely large files need a streaming parser: stream-json in Node, ijson in Python.

When JSON does become the bottleneck, in order: turn on HTTP compression, stop serialising fields nobody reads, and only then consider a binary format for internal service-to-service traffic.

Security, pointed at the right recursion

Build JSON with a serialiser, never with string concatenation. This is the whole of JSON injection.

Deep nesting is not a parser problem, at least not here.

Nesting depth measured on Node 24. JSON.parse accepted a million levels of nested objects without error, while a naive recursive walk over the parsed result exceeded the call stack at ten thousand
The mitigation everyone repeats is aimed at the component that survived.

The standard advice is to cap the parser's recursion depth. Measured on Node 24, JSON.parse swallowed a million levels of nested objects without complaint, because V8's parser is iterative and depth costs heap rather than stack. The three-line recursive function that walks the result afterwards threw RangeError: Maximum call stack size exceeded at ten thousand.

So the exposure is not the parser. It is every validator, sanitiser, redactor and pretty-printer you wrote as a recursive function. Cap request body size, and do not recurse over shapes you did not choose.

Prototype pollution is JavaScript-specific and narrower than its reputation: JSON.parse creates __proto__ as an ordinary own property, so parsing alone is safe. The risk arrives when you merge that object into another or use it as a lookup table. Prefer Map for untrusted keys.

Operations

Log one JSON object per line, which is ndjson, and every aggregator and jq will handle it:

{"ts":"2026-08-19T09:00:00Z","level":"info","msg":"user signed in","user_id":42}

Normalise before diffing. Sort keys and strip whitespace first, or the diff is formatting noise.

Do not put a large shared config in one JSON file. Every concurrent edit conflicts, and JSON merges badly. Split it, or use a format text merge tools cope with.

The attitude

JSON is boring, which is the best property an interchange format can have. The work is not understanding the grammar, it is knowing the edges: which dialect the file is in, what your parser does to a big integer, and which of your own functions is the recursive one.

Everything linked here runs in your browser and uploads nothing, which matters when the payload you are debugging is a production response. The cluster articles go deeper on each piece; this page exists to tell you which one you need.

Sources

Every number in this article traces to a source below. Where a claim could not be sourced, it was cut rather than softened.

  1. Primary sourceIETF

    The complete JSON grammar, which has no comment production, no trailing commas, no date type and no binary type.

  2. Primary sourceMicrosoft

    That the JSON with Comments mode is used for VS Code's own configuration files and accepts trailing commas while displaying a warning.

  3. Primary sourceEcma International

    That the Number type is the IEEE 754 double-precision 64-bit binary format, which is why integer precision in most JSON parsers stops being exact above 2 to the 53rd.

Topics

  • JSON
  • Developers
  • Guide
  • JWT
  • Base64
  • Debugging

Tools mentioned in this article

Get new tools by email

New tools and the occasional deep-dive, about once a month. No spam, no sharing your address, unsubscribe in one click.

Related articles

Article title card. A pair of large curly braces with an amber warning marker dropped between them, with the line: the parser knows where it broke
Developer Tools

Debugging Malformed JSON: A Field Guide

The parse error is better than its reputation. How to read what V8 says now, the five failure modes behind most broken payloads, and the two errors that hide their position.

Article title card. A pair of curly braces containing an amber double slash, with the line: the extension is not the format
Developer Tools

Why tsconfig.json Allows Comments and package.json Does Not

Both files end in .json. One accepts comments and trailing commas, the other throws. The difference is not the extension - it is which parser reads the file.

Article title card. Three grey squares and an arrow leading to four narrower amber bars, with the line: 3 bytes become 4 characters
Developer Tools

Base64 Explained: What It Is, Why It Exists, and When Not to Use It

What Base64 does to your bytes, the three variants that produce most decode failures, and the places it still earns its keep.