JSON is the format almost every API, config file and log line you touch is written in. This guide covers all of it: the JSON format itself, the six JSON data types, every syntax rule the specification actually contains, a worked JSON example taken apart, and an honest comparison with XML, YAML and CSV.
JSON is a text format for storing and exchanging structured data. It builds everything out of two containers — objects and arrays — and four simple values: strings, numbers, booleans and null.
The name stands for JavaScript Object Notation. Douglas Crockford specified it in the early 2000s by taking JavaScript's object literal syntax and discarding everything not needed to describe data. That subtraction is why it won: the grammar fits on one page, so every language shipped a parser quickly, and JSON is now fully language-independent.
Two standards define the same syntax —
RFC 8259
(December 2017, designated Internet Standard STD 90) and
ECMA-404,
second edition, from the same month. The IETF and Ecma keep them aligned, so there is
no practical difference. The registered media type is application/json;
the file extension is .json.
Despite the name, JSON is not JavaScript and is not executed. A JSON
document is inert text a parser reads as data — JSON.parse in JavaScript,
json.loads in Python, encoding/json in Go. Early code
evaluated JSON with eval(), which is exactly why real parsers replaced it.
Nor are the two quite the same language: until ES2019 a JSON string could hold a raw
U+2028 that a JavaScript literal could not, and ES2019 fixed that by relaxing
JavaScript, not JSON.
Here is a realistic customer order — the sort of payload a REST endpoint returns. Nearly every construct the format has appears in it.
{
"orderId": "A-10427",
"placedAt": "2026-08-06T14:32:09Z",
"customer": {
"id": 7741,
"name": "Ada Lovelace",
"vip": true
},
"items": [
{ "sku": "KB-01", "name": "Mechanical keyboard", "qty": 1, "price": 89.99 },
{ "sku": "CB-14", "name": "USB-C cable, 2 m", "qty": 3, "price": 7.5 }
],
"total": 112.49,
"currency": "GBP",
"coupon": null,
"tags": ["priority", "gift-wrap"]
}
Reading from the outside in: the outer braces make the document a single object, the usual shape for an API response. Inside are seven members — each a name, always a double-quoted string, then a colon, then a value, with commas between them but never after the last.
"orderId" holds a string, and so does
"placedAt": JSON has no date type, so the timestamp is an ISO 8601 string
by convention, not by rule. "customer" holds a
nested object — how JSON expresses arbitrary depth with only two
containers — inside which "id" is a number and
"vip" a boolean. "items" holds an
array: an ordered list whose two elements are objects,
and where order is part of the data in a way it is not for object members.
"coupon" is null, asserting the field exists and is empty
rather than missing.
That is the entire format: two containers, four leaf values, nothing else coming.
RFC 8259 counts them as four primitive types — string, number, boolean and null — plus two structured types, object and array, the only ones that can contain other values.
An unordered set of name/value pairs between { and }: a
double-quoted name, a colon, then any JSON value, with commas between pairs.
{} is a valid empty object.
{ "id": 7741, "name": "Ada", "active": true }
Pitfalls. Names must be quoted, unlike a JavaScript literal. RFC 8259 says names SHOULD be unique and warns that behaviour is unpredictable when they are not — most parsers keep the last duplicate, some the first. And since objects are unordered, never rely on key order surviving a parse.
An ordered sequence of values between [ and ], comma
separated; [] is valid. Elements need not share a type, though most APIs
keep them homogeneous.
[1, "two", true, null, { "n": 3 }, [4, 5]]
Pitfalls. No trailing comma before the closing bracket, and no holes —
JavaScript tolerates [1, , 3], JSON does not. Order is part of the data
here, so reordering changes the meaning.
Unicode characters in double quotes only, escaping the double quote,
the backslash and every control character below U+0020. The complete escape list is
short: \", \\, \/, \b,
\f, \n, \r, \t, and
\uXXXX with exactly four hexadecimal digits.
"She said \"hello\"\nPath: C:\\temp\nEmoji: \uD83D\uDE00"
Pitfalls. Single quotes are never valid, and a literal newline inside
a string must be written \n. Characters beyond the Basic Multilingual Plane
may appear literally or be escaped as a UTF-16 surrogate pair — the face above is
\uD83D\uDE00, never \u1F600, since a \u escape is
always exactly four hex digits. The \/ escape is optional, and exists so
embedded JSON cannot form a closing script tag.
Base ten: an optional minus sign, an integer part, an optional fraction and an optional exponent. That is the whole grammar.
0 -3 2.5 1e6 -1.6e-19 6.02E+23
Pitfalls. No leading plus, so +1 fails; no leading zeros,
so 01 fails; no hexadecimal or octal, so 0x1F fails. The
integer part cannot be omitted nor the fraction left empty, so .5 and
1. both fail. NaN and Infinity are explicitly not
permitted, which catches out anyone serialising floating-point results.
The bigger trap is precision. JSON sets no limit on range, but RFC 8259 notes that interoperability comes from expecting no more than IEEE 754 binary64 provides, and that only integers from −(253)+1 to 253−1 are agreed on exactly. A 64-bit database ID sails past that and JavaScript rounds it silently, so send large IDs as strings.
The bare literals true and false, lowercase and unquoted.
{ "active": true, "archived": false }
Pitfalls. True and TRUE belong to Python and
SQL. "true" in quotes is a string, and a truthiness check on
"false" passes.
A single lowercase literal, null. It means the value is present and empty,
a different statement from the key being absent altogether.
{ "coupon": null }
Pitfalls. NULL, None, nil and
undefined are all invalid. Decide deliberately whether your API omits empty
fields or sends null — clients written against one break against the
other.
A JSON document need not be an object or an array. RFC 4627 required that;
RFC 8259 defines a JSON text as any single value with optional surrounding
whitespace, so 42, "hello", true and
null are each valid JSON documents. Very old parsers may refuse them, and
most APIs use an object at the root anyway — an object can grow a field later without
breaking clients; a bare array or scalar cannot.
The whole grammar fits in a table. What a parser accepts is on the left; the mistakes that actually occur are on the right.
| Construct | Allowed | Not allowed |
|---|---|---|
| Object keys | Double-quoted strings: {"name": 1} |
{name: 1}, {'name': 1} |
| Strings | Double quotes: "text" |
'text', backticks, a raw newline inside the quotes |
| Escapes | \" \\ \/ \b \f \n \r \t \uXXXX |
\', \x41, \0, \u1F600 |
| Numbers | -1, 0, 1.5, 2e10, -1.6e-19 |
+1, 01, .5, 1., 0x1F, NaN, Infinity |
| Literals | true, false, null |
True, FALSE, NULL, None, nil, undefined |
| Commas | One between members or elements | A trailing comma before } or ]; a doubled comma |
| Whitespace | Space, tab, line feed, carriage return between tokens | Whitespace inside a number, a literal or an escape |
| Root value | Any single value, including a bare string or number | Two values side by side with nothing joining them |
| Encoding | UTF-8 | A byte order mark before the first character |
{name: "Ada"} is a JavaScript object literal, not JSON."2026-08-06T14:32:09Z". A Unix timestamp is the alternative — smaller, but unreadable and ambiguous about its units.undefined. Only null exists, and JSON.stringify quietly drops properties set to undefined — a real source of vanishing fields.JSON5 and JSONC add several of these back, but they are separate formats whose output a standard parser rejects. When a file will not parse, a JSON validator pinpoints which rule broke, and where.
A JSON file is plain text with a .json extension and no header, footer or
metadata. Whatever it contains is the entire document.
RFC 8259 requires JSON exchanged between systems outside a closed ecosystem to be encoded as UTF-8, and states that implementations must not add a byte order mark, though parsers may ignore one. That detail causes real grief: a file saved as UTF-8 with BOM begins with three invisible bytes, and a strict parser reports a mystifying error at line 1, column 1, since a JSON text may not start with anything but whitespace or a value. If a file looks perfect and still will not parse, check for a BOM first.
On the wire, JSON travels as an ordinary HTTP body labelled with the
application/json media type:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 53
{"orderId":"A-10427","total":112.49,"currency":"GBP"}
That header matters: clients such as fetch and requests use
it to decide whether to parse the body, so a server returning JSON labelled
text/html hands the caller a string instead of an object. Network payloads
are usually minified, since whitespace is pure transfer cost — which is why a raw API
response arrives as one long line and needs a
JSON formatter before a human can read it.
JSON Lines — also written JSONL, and near-identical to NDJSON, newline-delimited JSON —
puts one complete JSON value on each line of a UTF-8 file, separated by
\n, with the .jsonl extension by convention.
{"ts":"2026-08-06T14:32:09Z","level":"info","msg":"order created"}
{"ts":"2026-08-06T14:32:11Z","level":"warn","msg":"stock low","sku":"KB-01"}
{"ts":"2026-08-06T14:32:14Z","level":"info","msg":"payment captured"}
A JSON Lines file is not a single JSON document: pass one to an ordinary parser and it fails immediately, finding a second value where the document should have ended. That is the point of the format, not a flaw in it. Each line stands alone, so a process can append a record without rewriting the file, a reader can stream a huge dataset without holding it in memory, and a truncated file still yields every complete line before the break. Hence its use for logs, event streams and machine-learning datasets; to inspect one by hand, read a single line at a time.
These four cover almost all plain-text data interchange, and each is genuinely better than the rest at something.
| JSON | XML | YAML | CSV | |
|---|---|---|---|---|
| Readability | Good once indented | Noisy — every value is wrapped in a repeated tag pair | Best for humans; no brackets or quotes | Perfect for flat rows, useless for nesting |
| Comments | No | Yes | Yes | No |
| Data types | Six, built in | Text only unless a schema assigns types | JSON's six plus dates and timestamps | Text only; the reader guesses |
| Schema | JSON Schema — widely used, but not part of the JSON standard | XSD, DTD and RELAX NG; decades of tooling | Usually validated as JSON Schema after conversion | None; a header row at best |
| Size | Compact | Largest, by a wide margin | Comparable to JSON | Smallest for tabular data |
| Typical use | APIs, config, logs, NoSQL documents | Documents, publishing, SOAP, regulated formats | Hand-edited config: CI pipelines, Kubernetes, Docker Compose | Spreadsheet exports, bulk data loads |
When XML is the better choice. If you are modelling a document rather than a record — prose with markup interleaved through it — XML expresses mixed content natively and JSON cannot represent it at all. XML also brings attributes, namespaces and schema validation predating JSON Schema by a decade. Where a contract must be enforceable and auditable — finance, healthcare, government filings — XML is often the correct tool rather than legacy baggage.
When YAML is the better choice. For a file a human writes by hand, YAML
wins clearly: it has the comments configuration badly needs, drops the punctuation, and
handles multi-line strings instead of collapsing them into \n escapes.
Hence Kubernetes manifests, GitHub Actions workflows and Docker Compose files. The
trade-off is real — indentation is load-bearing, type inference has produced famous
surprises, and the fuller feature set has caused remote-code-execution bugs in careless
deserialisers. YAML for humans to write, JSON for machines to exchange, is a defensible
rule.
When CSV is the better choice. If the data is a rectangle — a million rows with identical columns — CSV is smaller, streams row by row, and opens straight in Excel; repeating every key name on every record, as JSON does, is pure waste at that scale. Its limits are equally clear: no nesting, no types, no real standard, endless ambiguity about quoting and delimiters. Use CSV where the shape is genuinely tabular, JSON the moment it is not.
data object with an optional errors array beside it.package.json for npm, tsconfig.json for TypeScript, composer.json for PHP, VS Code's own settings. The missing comments are felt constantly here, which is why VS Code invented JSONC.jsonb column brings the same model into a relational database.<script type="application/ld+json"> block describing what a page is about. This page carries one.localStorage holds strings only, so anything structured goes in through JSON.stringify and comes back out through JSON.parse.Everything here runs in your browser and nothing is uploaded, which matters when the payload you are debugging carries a bearer token or customer data.
Start with the JSON viewer to paste something and look at it. Use the JSON formatter to turn a minified API response into indented text with 2 spaces, 4 spaces or tabs. When a file will not parse, the JSON validator gives the reason with the exact line and column — usually enough to spot the trailing comma or single quote responsible. For deeply nested data, the JSON tree viewer collapses the branches you do not care about.
JSON stands for JavaScript Object Notation. The name records where the syntax came from, but the format itself is language-independent and is standardised separately, as RFC 8259 and ECMA-404.
No. JSON is a data format: a set of rules for writing structured data as text. It has no variables, no expressions and no way to describe behaviour, and a JSON document is parsed as data, never executed as code.
Object, array, string, number, boolean and null. RFC 8259 groups them as four primitive types — string, number, boolean and null — plus two structured types, object and array, the only two that can contain other values.
No. The grammar has no comment syntax, so any conforming parser rejects a document containing one. The usual workarounds are an ordinary key such as _comment, stripping comments before parsing, or using a format that supports them, such as JSON5, JSONC or YAML.
JSON has no date type, so dates are stored as strings, and the near-universal convention is ISO 8601 in UTC, such as 2026-08-06T14:32:09Z. A Unix timestamp as a number is the alternative: compact, but unreadable and ambiguous about seconds versus milliseconds.
JSON is smaller, quicker to parse and maps directly onto the arrays and dictionaries most languages already have. XML is more verbose but carries features JSON lacks — comments, attributes, namespaces, mixed content and mature schema validation — which is why it survives in documents and regulated formats.
A .json file is plain UTF-8 text, so any text editor opens it, and a code editor such as VS Code adds syntax highlighting and folding. For a minified or very large file, paste it into a JSON viewer, which indents the text and lets you collapse the nesting.
No. A JavaScript object is a value in memory; JSON is text. JSON is also stricter: keys must be double-quoted strings, single quotes are never allowed, and functions, undefined, comments and trailing commas — all legal in a JavaScript object literal — are not part of JSON.