JSON in 10 Minutes

JSON is a tiny data format with strict punctuation and surprisingly expensive edge cases. Learn the six values it can represent, validate at system boundaries, and stop pretending dates or giant integers are native JSON types.

🎙️ Published & recorded: ·

01JSON has six value types

JSON can hold an object, array, string, number, boolean, or null. That is the entire type system. There is no date, byte array, set, comment, undefined value, or special integer type. Objects map string keys to values. Arrays preserve order. Object key order should never carry meaning, even though many parsers happen to retain it.

{
  "name": "Mina",
  "score": 9.5,
  "active": true,
  "middle_name": null,
  "skills": ["SQL", "Python"],
  "address": { "city": "Leeds" }
}
Null is not missing

{"middle_name": null} explicitly supplies a key with no value. {} omits the key. APIs often use that distinction for “clear this field” versus “leave it unchanged.” Decide the meaning in your contract; do not let individual clients guess.

02The syntax is strict on purpose

Keys and strings require double quotes. Commas separate members, but a trailing comma is illegal. Comments are illegal. Numbers cannot be NaN or Infinity. JSON is a wire format, not a friendly hand-edited configuration language. If people edit the file every day, use TOML or YAML and validate it; do not invent JSON with comments.

// Invalid JSON
{ 'port': 3000, "debug": true, }

// Valid JSON
{ "port": 3000, "debug": true }
Unexpected token, fixed

Chrome may report SyntaxError: Unexpected token ' in JSON at position 2. One: inspect the character at the reported position. Two: replace single-quoted keys and strings with double-quoted ones. Three: remove trailing commas and comments. Four: run python -m json.tool settings.json. It prints formatted JSON on success and a precise line and column on failure.

03Nest for ownership, not cleverness

Nesting is useful when the child belongs to the parent. A shipping address belongs inside an order snapshot. Fifty levels of generic data, attributes, and items do not make an API flexible; they make every consumer defensive. Keep stable identifiers near the top and use arrays only when repeated values really have an order.

{
  "order_id": "ord_204",
  "customer_id": "cus_18",
  "shipping_address": {
    "line1": "14 King Street", "city": "Leeds"
  },
  "items": [
    { "sku": "BK-7", "quantity": 2 }
  ]
}

Access nested data defensively when the contract permits absence. In JavaScript, order.shipping_address?.city ?? "unknown" handles a missing object. It does not excuse an undocumented shape. Publish examples and a schema so clients know which keys are required.

04Parse input; serialize output

Parsing turns JSON text into language values. Serialization does the reverse. Keep that boundary visible. In JavaScript use JSON.parse and JSON.stringify. In Python use json.loads for a string, json.load for a file, and the matching dump functions for output. Never build JSON by concatenating strings; escaping will betray you.

// JavaScript
const user = JSON.parse('{"name":"Mina","active":true}');
const text = JSON.stringify(user, null, 2);

# Python
import json
user = json.loads('{"name":"Mina","active":true}')
text = json.dumps(user, indent=2, ensure_ascii=False)
Two real serialization failures

Python reports json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) for {'name':'Mina'}. One: confirm the input is JSON, not a printed Python dictionary. Two: change quotes at the producer, not with a blind string replacement. Three: validate with python -m json.tool. JavaScript reports TypeError: Converting circular structure to JSON when an object points back to itself. One: inspect the cycle named in the error. Two: serialize a deliberate plain object or replace the back-reference with an ID. Three: do not hide the cycle with a replacer unless losing that field is part of the contract.

05Validate the shape with JSON Schema

Valid JSON can still be useless data. A parser accepts a negative quantity and a misspelled email key because both are legal syntax. JSON Schema describes required fields, types, formats, ranges, and whether unknown properties are allowed. Validate at every trust boundary. Inside your own process, use normal typed models instead of revalidating the same object forever.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "required": ["sku", "quantity"],
  "properties": {
    "sku": { "type": "string", "minLength": 1 },
    "quantity": { "type": "integer", "minimum": 1 }
  },
  "additionalProperties": false
}

My default for small request objects is additionalProperties: false. Silent typos are worse than a clear four-hundred response. For public versioned APIs, add fields compatibly and make clients tolerate fields they do not know. Those goals conflict, so choose the rule deliberately rather than copying a schema switch.

06Use JSONL for streams and logs

A normal JSON document is one complete value. A million-record array usually must be parsed as a whole and becomes awkward to append. JSON Lines, also called JSONL or NDJSON, stores one complete JSON value per line. It is better for logs, exports, pipelines, and incremental processing. It is not valid as one ordinary JSON document, so label the format honestly.

# users.jsonl
{"id":1,"name":"Mina"}
{"id":2,"name":"Luis"}
{"id":3,"name":"Asha"}

# Process one record at a time
with open("users.jsonl", encoding="utf-8") as rows:
    for line in rows:
        user = json.loads(line)
Choose by access pattern

Use .json for a settings object or API response that must be complete. Use .jsonl when records are independent and you want to append, stream, split, or recover after a bad line. Do not wrap JSONL lines in commas or square brackets. That turns it back into an array and removes the streaming advantage.

07JSON over HTTP needs an explicit contract

Send JSON with Content-Type: application/json. Use Accept: application/json when the server can return several formats. Check the HTTP status before parsing the body, because a proxy may return an HTML error page. A successful parse says nothing about business success; status codes and response fields still matter.

curl --fail-with-body https://api.example.com/orders \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  --data '{"sku":"BK-7","quantity":2}'

HTTP/1.1 201 Created
Content-Type: application/json; charset=utf-8

{"order_id":"ord_204","status":"accepted"}
HTML masquerading as JSON

SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON usually means the response is HTML. One: log the status and received Content-Type. Two: inspect the raw body. Three: fix the URL, authentication, or proxy error that produced the HTML page. Four: parse JSON only when the response contract says JSON. Do not trim the angle bracket and retry the parser.

08Big integers and dates need conventions

JSON numbers do not promise a bit width, but JavaScript numbers lose integer precision beyond nine quadrillion and change. An identifier is not arithmetic, so send large IDs as strings. JSON has no date type either. Send a clear ISO 8601 timestamp with a timezone, preferably UTC ending in Z. A date-only value such as a birthday should stay a date-only string.

{
  "order_id": "9223372036854775807",
  "created_at": "2026-07-25T14:05:00Z",
  "birthday": "1994-03-12",
  "amount_minor": 1299,
  "currency": "GBP"
}

For money, send integer minor units plus a currency when the currency has a conventional minor unit. For arbitrary decimal precision, send a decimal string and document its scale. Never send a local timestamp like 2026-07-25 09:00 without an offset. Two clients can parse it as two different instants and both believe they are correct.

09Treat JSON as untrusted input

Parsing JSON is safer than evaluating code, but the resulting data is still controlled by the sender. Limit body size and nesting depth, validate the schema, authorize the requested action, and escape values for the destination where you use them. JSON does not prevent SQL injection, path traversal, cross-site scripting, or prototype pollution.

// Good boundary order
limitBody("256kb");
const value = JSON.parse(rawText);
validateSchema(value);
authorize(request.user, value.order_id);
await db.query("SELECT * FROM orders WHERE id = $1", [value.order_id]);

// Never do this
eval("(" + rawText + ")");
Object.assign(globalDefaults, value);
The parser is not a firewall

Reject oversized bodies before buffering them. Reject unexpected keys before merging objects, especially keys such as __proto__, constructor, and prototype in JavaScript systems. Use parameterized database queries. Escape output for HTML when rendering it. Redact passwords, tokens, and personal data before logging. The right question is not “Did JSON.parse accept it?” but “Is this value permitted here?”

10JSON cheat sheet

Keep this compact reference beside the code that owns your boundary.

# six values
object  {}    array  []    string  "text"
number  12.5  boolean true false  null null

# strict rules
double quotes · no trailing comma · no comments · UTF-8
no undefined · no NaN or Infinity · keys are strings

# JavaScript
JSON.parse(text)            JSON.stringify(value, null, 2)

# Python
json.loads(text)            json.dumps(value, indent=2)
python -m json.tool file.json

# HTTP and modeling
Content-Type: application/json
large ID → string           timestamp → ISO 8601 with zone
stream of records → JSONL   untrusted input → limit + validate

My opinionated rule is simple: JSON should be boring at the boundary. Use strings for IDs, explicit timestamps, schemas for incoming data, and JSONL for record streams. If your format needs comments, references, custom date literals, and five decoding conventions, it is no longer a simple JSON contract. Pick a format that admits what the data really is.

Tell me what missed

A correction is more useful than a compliment. This goes straight to the person who writes SwiftGrasp.

Was this page useful?
0/1000

Please do not include passwords, private keys, or personal information.