All Tools View Categories Blog About Contact Privacy

JSON Explained: A Complete Beginner's Guide to the Format That Runs the Web

JSON Explained: A Complete Beginner's Guide to the Format That Runs the Web

JSON is text that looks like a JavaScript object — {"name":"Ada","age":30} — and it's how most of the web talks: fetch an API, get JSON, JSON.parse to a JavaScript object; save settings to package.json, also JSON. This beginner's guide explains syntax (6 value types, no single quotes), how to read an API response, validate trailing commas, and convert JSON to CSV — no prior JSON needed.

TL;DR — JSON:
  • What: JavaScript Object Notation — UTF-8 text, not binary — 6 value types: string "hi", number 42, boolean true/false, null, object {}, array []; specified by json.org and RFC 8259.
  • Syntax: keys must be "quoted" strings, no single quotes, no trailing comma, no comments, no undefined{"name":"Ada"} not {name:'Ada',}.
  • Run the web: fetch("/api/user") → JSON {"user":{"id":42,"name":"Ada"}}data.user.name in JS, data["posts"][0] in Python; config package.json, tsconfig.json, OpenAPI, GeoJSON are all JSON.
  • Tools: paste into a JSON formatter validator to highlight line 3 col 18 error and pretty-print, and into a convert JSON to CSV to turn [{"name":"Ada","age":30}]name,age\nAda,30 with correct quoting.
  • Quick test: JSON.parse('{"a":1}') → object; JSON.stringify({a:1}) → text — the two directions you use daily.

What Is JSON — Text That Looks Like a JavaScript Object (But Isn't Code)

JSON (JavaScript Object Notation) is plain text — you can open data.json in Notepad and read it — that uses the same literal syntax as JavaScript objects and arrays but strictly as text, not code to execute. That makes it human-readable ("name": "Ada"), machine-parseable in every language, and safe (no function calls).

It was standardized as ECMA-404 and RFC 8259. Example:

{"name": "Ada Lovelace", "age": 30, "verified": true}

That's a UTF-8 text file with 38 bytes — not binary like an image. APIs, config, and storage all use the same form.

What is JSON text vs binary where you meet it APIs config

Where You Meet JSON Every Day

  • APIs (REST): fetch("https://api.example.com/user/42") → body is JSON → JSON.parse(text)data.name. Nearly every public API defaults to JSON; see MDN: JSON.
  • Config: package.json (npm), tsconfig.json, OpenAPI openapi.json, GeoJSON for maps — all JSON.
  • Storage: browsers localStorage stores JSON stringified; NoSQL (MongoDB) logs JSON.

JSON vs XML: {"name":"Ada"} (21 bytes) vs <name>Ada</name> (23+ with tags) — JSON is lighter, faster, and native to JavaScript (JSON.parse built-in) while XML needs a DOM parser. That's why JSON won the web (XML still in SOAP).

Text, Not Binary — Open It Anywhere

Save the snippet above as person.json (UTF-8) → open in Notepad → you see {"name": "Ada"}. Change "Ada""Bob" and save — still valid JSON if quoting and commas hold. Try: echo '{"a":1}' | python -m json.tool pretty-prints in terminal.

Syntax — Objects {}, Arrays [], and 6 Value Types Only

Everything is one of six:

  1. String — double-quoted "Ada" (not 'Ada'), escapes \", \n, \t, \uXXXX.
  2. Number30, 3.14, -42, 1e10 — no leading zero 0123, no NaN/Infinity.
  3. Booleantrue or false lowercase.
  4. Nullnull (no undefined).
  5. Object{"key": value, ...} unordered name/value pairs — name must be string.
  6. Array[1,2,3] ordered list.
{
  "name": "Ada",                // string
  "age": 30,                     // number
  "active": true,                // boolean
  "spouse": null,                // null
  "tags": ["js", "json"],        // array of strings
  "address": { "city": "London" } // nested object
}
JSON syntax objects arrays 6 value types keys must be quoted

Rules: keys must be "quoted" strings — {name: "Ada"} is JavaScript but not JSON → parser: Unexpected token n. No trailing comma — {"a":1,}Unexpected token }. No single quotes — {'a':1}Unexpected token '. No comments — // comment not allowed (use JSON5 if you need comments). No undefined — use null.

Dates Are Strings

JSON has no Date type — dates are ISO strings: {"created": "2024-01-01T00:00:00Z"} → parse with new Date(data.created). Storing "2024-01-01" bare without quotes → Unexpected token 2 (parser sees minus).

Types Deep — Strings Must Be Quoted, Numbers Only, Null Not Undefined

Beginner errors, with correct next to it:

// Wrong → Error at line:col                       // Right
{"name": 'Ada'}       // ✗ single quotes          {"name": "Ada"}
{"age": 30,}          // ✗ trailing comma         {"age": 30}
{name: "Ada"}        // ✗ unquoted key           {"name": "Ada"}
{"active": undefined} // ✗ undefined not allowed  {"active": null}
{"date": 2024-01-01}  // ✗ bare date              {"date": "2024-01-01T00:00:00Z"}
Common JSON errors single quotes trailing comma undefined

Strings win for big ints: JavaScript numbers lose precision beyond 2^53-1 (9007199254740991). API that returns {"id": 9007199254740993} round-trips as 9007199254740992 → use {"id": "9007199254740993"} as string, keep as BigInt.

Real Example — API Response You Actually See

{
  "user": {
    "id": 42,
    "name": "Ada Lovelace",
    "verified": true,
    "tags": ["admin", "editor"]
  },
  "posts": [
    { "id": 1, "title": "Hello JSON", "views": 1234 },
    { "id": 2, "title": "Second post", "views": 567 }
  ]
}
Real API response user posts array nested object

Read: top level object → user is object → tags is array of two strings; posts array of 2 objects. Access: JS data.user.name → "Ada Lovelace", data.posts[0].title → "Hello JSON"; Python data["user"]["name"], data["posts"][0]["title"]. This shape repeats everywhere: fetchresponse.json() (does JSON.parse for you) → object. See MDN Fetch + JSON.

In the wild: package.json with "dependencies": {"react": "^18.0.0"} → nested object where keys are package names.

Nested and Arrays — Flatten for CSV

Nested: {"address":{"city":"London","zip":"W1"}} → for CSV, flatten to address_city="London" with dot or underscore, because CSV has no nesting. See conversion next.

How JSON Runs the Web — Fetch, Config, and Storage

Three places JSON is glue:

  1. Fetch API: fetch("/api/user/42").then(r => r.json()).then(data => console.log(data.user.name))r.json() parses. JSON.parse and JSON.stringify are built-ins. See Fetch.
  2. Config: tsconfig.json {"compilerOptions": {"target": "ES2020"}} → TypeScript reads object. Invalid JSON (trailing comma) → TS18002: Expected ','.
  3. Storage: localStorage.setItem("user", JSON.stringify(user)) → retrieve JSON.parse(localStorage.getItem("user")) — storage is string-only, JSON bridges object→string. Even logs are often JSON lines ({"level":"info","msg":"request"}) for parsers.

Compare to YAML: YAML allows comments and unquoted keys but indentation-sensitive — many projects keep JSON for strict interchange, YAML for human config. Both convert.

Parse and Stringify — The Two Directions You Use Daily

// Text → Object (receive)
const text = '{"name":"Ada","age":30}'
const obj = JSON.parse(text) // { name: "Ada", age: 30 }
console.log(obj.name) // Ada

// Object → Text (send)
const user = { name: "Ada", age: 30 }
const json = JSON.stringify(user) // '{"name":"Ada","age":30}'
fetch("/api/user", { method: "POST", body: json, headers: { "Content-Type": "application/json" } })

// Pretty for humans: 2-space indent
JSON.stringify(user, null, 2)
// {
//   "name": "Ada",
//   "age": 30
// }

JSON.stringify drops undefined, function, Symbol{a: undefined, b: () => {}}{}. Use null instead. See MDN stringify replacer.

Validate and Format — Fix Trailing Commas and Quotes in One Paste

Paste minified {"a":1,"b":2,} → validator says Unexpected token } at line 1 col 13 — remove trailing , → valid. Paste {'a':1}Unexpected token ' → replace ' with " and quote key.

Validate invalid trailing comma format pretty print

Paste into a JSON formatter validator to highlight line 3 col 18 error, auto-fix quotes, and pretty-print with 2-space indent — no guessing which comma. Tip: also check echo '{"a":1}' | python -m json.tool in terminal.

Schema Validation — Beyond Syntax

Syntax valid doesn't mean shape correct — {"age": "thirty"} is valid JSON but wrong type. Use JSON Schema (json-schema.org) or Zod/Joi to validate age is number, name is string at parse.

Convert — JSON ↔ CSV (One Array of Objects = One Table)

An array of flat objects is a table: [{"name":"Ada","age":30},{"name":"Bob","age":25}] ↔ CSV:

name,age
Ada,30
Bob,25
JSON array to CSV name age convert table nested flatten

Rules: header row = keys, rows = values, quoted if comma/newline/quote inside. Nested: flatten {"address":{"city":"London"}}address_city=London with dot/underscore, because CSV has no nesting. Paste array into a convert JSON to CSV → choose delimiter comma vs semicolon (EU Excel) → download CSV — it handles quoted commas and newlines correctly per RFC 4180 CSV.

When to Choose Which

JSON for APIs/config/nesting, CSV for spreadsheets/tables. Convert when analyst wants Excel: API JSON → CSV for pivot.

Common Pitfalls — 5 Before You Commit Valid JSON

MistakeFix
Single quotes'Ada'"Ada"
Trailing comma{"a":1,} → remove before } or ]
Unquoted key{name:"Ada"}{"name":"Ada"}
CommentsUse JSON5 or strip — JSON has no //
Date bare{"d":2024-01-01}{"d":"2024-01-01T00:00:00Z"}

Strings, Numbers, and Booleans — Rules That Trip Beginners

Strings: double-quoted only, escapes \", \\, \/, \b, \f, \n, \r, \t, \uXXXX. Example {"quote": "She said \"hi\""} → value She said "hi". Single quote fails: {'a':1} → parser error. Unescaped newline inside string fails — use \n.

Numbers: 42, 3.14, -0.5, 1e10 allowed — no leading zero 0123Unexpected number, no NaN or Infinity (use null or string "NaN"). Leading 0 only as 0 or 0.5.

Booleans and null: lowercase only true/false/nullTrue, False, NULL, NONE fail. No undefinedJSON.stringify({a: undefined}) drops key → {"b":1} not {"a":null}.

Real Workflow — Fetch, Validate, Store, Convert (End-to-End)

  1. Fetch: const res = await fetch("https://api.example.com/users"); const data = await res.json(); // does JSON.parse — check res.ok before parse, or catch Unexpected token < when API returned HTML error, not JSON.
  2. Validate: paste response into formatter validator → highlights line 1 col 1 if HTML — fix endpoint. Check type: if (typeof data.age !== 'number') throw before using — syntax valid doesn't mean shape correct.
  3. Store: localStorage.setItem("user", JSON.stringify(user)) → retrieve JSON.parse(localStorage.getItem("user") || "null") — storage is string-only, JSON bridges object↔string. Without stringify, setItem("user", user) stores [object Object].
  4. Convert: need Excel? Array [{"name":"Ada","age":30}] → CSV name,age\nAda,30 via converter — delimiter per RFC 4180, quote commas/newlines correctly. Nested flatten as above.

Pretty vs Minified — When Each Matters

// Minified (wire): {"user":{"id":42,"name":"Ada"}}
// Pretty (human): {
//   "user": {
//     "id": 42,
//     "name": "Ada"
//   }
// }
JSON.stringify(user)      // minified, no spaces — smaller for network
JSON.stringify(user, null, 2) // pretty, 2-space indent — committed config, logs

Minified saves bytes on wire (APIs); pretty is for committed package.json, tsconfig.json and logs — diff friendly. Formatter pretty-prints minified in one paste before you read it.

JSON5, JSONC, and Comments — When Pure JSON Is Too Strict

Pure JSON per RFC 8259 forbids comments and single quotes — {"a":1} // comment fails. Two supersets allow them for config:

  • JSON5: single quotes, trailing commas, comments //{'a':1, // comment } valid → used in some configs.
  • JSONC (JSON with Comments): VS Code's tsconfig.json and settings.json allow // and trailing commas — editor parses, interchange must be pure.

Rule: store config as JSONC for humans if your tool supports it, but send pure JSON over network — strip comments before fetch or API will 400. See JSON5.

Check Before You Send — Copy These 3:
1) Keys quoted? {"name":1} not {name:1}
2) Trailing comma? {"a":1,} → remove before } or ]
3) Single quotes? {'a':1} → "a"
Paste into validator after each fix — line:col tells exact char.

JSON Schema — Syntax Valid ≠ Shape Correct

{"age": "thirty"} passes parser but fails app (age should be number). JSON Schema validates shape after syntax:

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": { "type": "number", "minimum": 0 }
  },
  "required": ["name", "age"]
}

Validate with json-schema validator or Zod in JS: z.object({name: z.string(), age: z.number()}).parse(data) — throws before you use data.age + 1 on string. Syntax is line 1, schema is line 2 — both needed.

Escaping, Unicode, and Big Numbers — What parse Does

  • Unicode: {"city": "M\u00FCnchen"} is valid JSON — \u00FC decodes to üMünchen. JSON.parse handles \u automatically. Don't double-escape.
  • Big numbers: 9007199254740993 loses in JS (becomes 9007199254740992) — store as string "9007199254740993" and use BigInt or string compare.
  • Duplicate keys: {"a":1,"a":2} is valid JSON per spec but behavior undefined — last wins in JS. Avoid.

Security — Don't Trust JSON You Didn't Validate

JSON from user or third-party is untrusted — parsing alone doesn't sanitize. Example attack: {"__proto__": {"isAdmin": true}} proto pollution if you merge blindly via Object.assign — use Object.create(null) or check __proto__. Log injection: {"msg": "hi\n[error] fake"} with newline inside string → log parser splits line — validate \n handling. Always validate shape before use, and never eval JSON — use JSON.parse only. See OWASP Deserialization.

Performance and Size — Minify, Gzip, and Streaming

APIs send minified (no spaces) + gzip (JSON compresses well — 70% typical). For large arrays (100K rows), don't JSON.parse 10MB at once — stream via JSONStream or NDJSON ({"id":1}\n{"id":2}) per line. Pretty print for humans, minify for wire, stream for large. See MDN stringify space param.

NDJSON vs JSON Array

[{"id":1},{"id":2}] needs full parse; {"id":1}\n{"id":2} (NDJSON) parses line-by-line for logs (jsonl) — choose array for small, NDJSON for logs/stream. See ndjson.org.

Audit Before You Commit — 3 Lines:
1) echo '{"a":1,}' | python -m json.tool  # fails trailing , → fix
2) cat data.json | jq .  # pretty + validate + query
3) JSON.stringify(obj, null, 2) → diff friendly committed config
Use jq to query: cat api.json | jq '.user.name' → Ada.

History and Why JSON Won — Douglas Crockford to RFC 8259

JSON was formalized by Douglas Crockford in 2001 as a subset of JavaScript literal syntax, then standardized via json.org and RFC 8259 (2017, obsoletes RFC 7159). It won because it's what JS already did — JSON.parse is native, no XML DOM, no custom parser. Every language added json.loads (Python), JSON.parse (JS), json.Unmarshal (Go) with same text. For interchange, dumb text beats smart binary (MessagePack) on debuggability — you open it in Notepad.

When JSON Is Not the Answer — YAML, TOML, CSV

JSON is great for machine interchange, weaker for human config where comments and trailing commas matter. YAML allows # comment, unquoted keys, multiline | — good for Kubernetes manifests but indentation-sensitive (2-space trap). TOML allows comments and tables — good for Cargo.toml. CSV is for flat tables, not nesting. Choose: API/storage → JSON; human config with comments → JSONC/YAML; table → CSV via converter above.

Reading Real Errors — Line 3 Col 18 Means What

Validator reports: SyntaxError: Unexpected token } at line 3 col 18 → count chars: line 3 is "age": 30, // comma before } on next line → col 18 is the } after , — remove comma. Another: Unexpected token ' at col 2 → you used ' not ". Fix, paste again → Valid JSON, 3 keys.

Production pattern — Validate on Boot, Not at 3am:
// config.js — fail fast
import { readFileSync } from 'fs'
const raw = readFileSync('config.json', 'utf8')
try {
  const cfg = JSON.parse(raw)
  if (typeof cfg.port !== 'number') throw new Error('port must be number')
} catch (e) {
  console.error('Invalid config.json:', e.message)
  process.exit(1)
}
Catch line:col before service starts — not after deploy.

jq and Command Line — Query JSON Without Code

Command line JSON via jq (like grep for JSON):

cat api.json | jq '.user.name'          # → "Ada"
cat api.json | jq '.posts | length'     # → 2
cat api.json | jq '.posts[] | .title'   # each title
echo '{"a":1}' | jq .                  # pretty + validate

jq is the fastest way to check "is this valid and what key is null" before opening an editor. Alternative: python -m json.tool.

Common Questions — Top and Nested Access

Top level must be object or array — {"a":1} or [1,2], not bare 1 alone (some parsers allow, RFC prefers container). Nested access: data["address"]["city"] if key has dot. Dynamic key: data[keyVar] not data.keyVar.

Keep one source: committed pretty JSON (2-space) for diffs, wire minified for size — formatter toggles both.

Record Keeping — Version and Validate in CI

Commit pretty JSON (2-space) and validate on CI: python -m json.tool config.json > /dev/null && echo ok || fail or jq empty config.json. Pre-commit hook: fail if .json invalid before push. Keep package.json sorted — formatter keeps keys order.

Version your openapi.json like code — git diff on pretty shows which endpoint added age type change.

Copy working JSON block as template — one correct {"name":"Ada"} reused beats four hand-typed variants with different quote mistakes.

Keep one source: committed pretty for diffs, wire minified for size — formatter toggles both in one paste.

Bonus: keep package.json sorted and 2-space — npm install already does; diff then shows real change, not reformat noise.

Validate early, fail fast at boot — not at 3am when JSON.parse on bad file crashes request.

Keep one source: committed pretty for diffs, wire minified for size — choose per context, not per file.

Copy working {"a":1} template — one correct reused beats four hand-typed with different trailing commas.

Version your JSON like code — git diff on pretty shows which key was added when age changed.

Keep one source — committed pretty for diffs, wire minified for size — choose per context.

Validate at build — jq empty fails fast before deploy.

Frequently Asked Questions

What is JSON?

JavaScript Object Notation — UTF-8 text using {} and [] with 6 value types (string, number, boolean, null, object, array), specified by RFC 8259 and json.org — used for APIs, config, and storage because it's human and machine readable.

Is JSON the same as a JavaScript object?

No: JSON is text ('{"a":1}' string), a JavaScript object is live memory ({a:1}). JSON.parse(text) → object, JSON.stringify(obj) → text.

Why must JSON keys be quoted?

Spec requires string keys ({"key":1}). Unquoted {key:1} is JavaScript object literal, not JSON — parsers throw Unexpected token k.

Can JSON have comments?

No — strictly per RFC 8259. Use JSON5 (// comment allowed) or JSONC for config comments, but interchange should be pure JSON.

How do I fix JSON trailing comma error?

Remove comma before } or ]{"a":1, "b":2,}{"a":1,"b":2}. Validate via formatter validator to highlight line:col.

When should I use JSON vs CSV?

JSON for nested APIs/config — preserves structure; CSV for flat tables for Excel. An array of flat JSON objects converts cleanly to CSV rows; nested JSON needs flattening.