Getting Started
Basic Structure
JSON supports 6 data types: string, number, boolean, null, array, and object. Keys must be strings in double quotes. JSON is language-independent and easy for humans and machines to read.
{
"name": "Alice",
"age": 30,
"isStudent": false,
"height": 1.65,
"nullValue": null,
"hobbies": ["reading", "coding", "music"],
"address": {
"city": "NYC",
"zip": "10001"
}
}JSON Syntax Rules
JSON has strict syntax: keys and strings use double quotes only, commas separate elements, and trailing commas are forbidden. The entire document must be a single JSON value. Standard JSON has no comments—use a preprocessor or JSON5 if you need them.
{
"key": "value", // key MUST be a double-quoted string
"nested": { "a": 1 }, // objects use { } with comma-separated pairs
"list": [1, 2, 3] // arrays use [ ] with comma-separated values
}
// Rules:
// - Double quotes only for strings and keys (no single quotes)
// - Comma separates members; NO trailing comma
// - The whole document is ONE value (usually an object or array)
// - No comments allowed in standard JSONComments in JSON
Standard JSON forbids comments. Common workarounds: add a '_comment' field (ignored by code), use JSONC/JSON5 (supported by VS Code, many tools), or strip comments before parsing with a library like comment-json or JSON.minify.
// Standard JSON does NOT allow comments.
// Workaround 1: include a descriptive field
{
"_comment": "This is a config file for the app",
"port": 8080
}
// Workaround 2: use JSON5 or JSONC (VS Code settings)
{
// line comment
"port": 8080 /* block comment */
}
// Workaround 3: strip comments before parsing
// (e.g., JSON.minify, comment-json npm package)Whitespace & Formatting
Whitespace outside strings is insignificant—compact and pretty forms parse to the same value. Compact JSON saves bandwidth; pretty-printed (2-space indent is common) aids readability and diffs. Most serializers offer a 'pretty' option.
// These are all equivalent:
{"a":1,"b":2} // compact (no whitespace)
{ "a": 1, "b": 2 } // readable
{
"a": 1,
"b": 2
} // pretty-printed
// Whitespace between tokens is insignificant.
// Use compact for transmission, pretty for humans.File Extension & MIME Type
The standard file extension is .json and the MIME type is application/json. Variants like JSONC, JSON5, GeoJSON, and NDJSON have their own conventions. Always use UTF-8 encoding; the BOM is discouraged.
// File extensions:
// .json standard JSON
// .jsonc JSON with comments (VS Code, TypeScript config)
// .json5 JSON5 (relaxed syntax)
// .geojson GeoJSON (geographic data)
// .ndjson Newline-Delimited JSON (JSON Lines)
// .har HTTP Archive (JSON-based)
// MIME type:
Content-Type: application/json
// Older/alternate MIME types (avoid):
// text/json (non-standard)
// application/x-json (non-standard)
// UTF-8 is the default and recommended encoding.JSON Value Categories
JSON values fall into six types: string, number, boolean, null, array, object. Arrays and objects are structured (can hold other values); the rest are primitives. A valid JSON document is one value—commonly an object or array, but a bare number or string is also valid.
// A JSON value is exactly one of these six types:
"hello" // string
42 // number
3.14 // number
true // boolean
false // boolean
null // null
[1, 2, 3] // array
{ "a": 1 } // object
// Structured types: array, object
// Primitive types: string, number, boolean, null
// A standalone JSON document is a single value of any type.Data Types
String Type
JSON strings are double-quoted Unicode text. Common escapes: \" (quote), \\ (backslash), \/ (slash), \n, \t, \r, \b, \f, and \uXXXX for any Unicode code point. Single quotes are not allowed as string delimiters.
{
"single": "double quotes only",
"empty": "",
"escaped": "He said \"hi\" and C:\\Users",
"unicode": "Cafu00e9 \u00e9 \u1F600",
"multiline": "line1\nline2\ttabbed"
}
// Strings MUST use double quotes.
// Single quotes are NOT valid in JSON.Number Type
JSON numbers follow a subset of IEEE 754 double-precision: integers, decimals, and scientific notation (e.g., 6.022e23). Leading zeros are forbidden (007 is invalid). NaN, Infinity, and -Infinity are NOT valid—encode them as null or strings if needed.
{
"integer": 42,
"negative": -7,
"float": 3.14,
"zero": 0,
"scientific": 6.022e23,
"small": 1.5e-10
}
// Numbers are NOT quoted.
// No leading zeros (007 is invalid).
// No NaN, Infinity, or -Infinity (not valid JSON).Boolean Type
JSON booleans are the literal keywords true and false (lowercase, unquoted). Quoted 'true'/'false' are strings. JSON has no concept of 0/1 or yes/no as booleans—convert them in your application code.
{
"enabled": true,
"disabled": false,
"flag": true
}
// Booleans are the bare keywords true and false.
// They are NOT quoted ("true" is a string).
// 0/1, yes/no, on/off are NOT booleans in JSON.Null Type
JSON null is the bare keyword null (unquoted). It signals the intentional absence of a value. Note that null is distinct from an empty string '', 0, false, or a missing key. Many languages map null to None/nil/null.
{
"middleName": null,
"deletedAt": null,
"optional": null
}
// null is a bare keyword (unquoted).
// "null" (quoted) is a string, not null.
// null represents the intentional absence of value.Array Type
JSON arrays are ordered lists wrapped in [ ], comma-separated, zero-indexed. They can hold any JSON value, including mixed types and nested arrays/objects. Empty arrays [] are valid. Order is preserved and significant.
{
"numbers": [1, 2, 3],
"mixed": [1, "two", true, null],
"nested": [[1, 2], [3, 4]],
"objects": [
{ "id": 1 },
{ "id": 2 }
],
"empty": []
}
// Arrays are ordered, zero-indexed, and can hold mixed types.Object Type
JSON objects are unordered key/value collections wrapped in { }. Keys must be unique double-quoted strings; values can be any JSON type. Although objects are conceptually unordered, most parsers preserve insertion order; do not rely on order for correctness.
{
"user": {
"id": 1,
"name": "Alice",
"roles": ["admin", "user"],
"active": true
},
"empty": {}
}
// Objects are unordered collections of key/value pairs.
// Keys MUST be unique strings (double-quoted).
// Values can be any JSON type.Strings
String Basics
JSON strings are double-quoted sequences of Unicode characters. The forward slash can be escaped (\/) but does not need to be—useful when embedding JSON in <script> tags. Empty strings are valid.
{
"name": "Alice",
"email": "[email protected]",
"url": "https://example.com/path?q=1",
"path": "C:\\Users\\alice",
"empty": ""
}
// Strings are double-quoted Unicode text.
// Forward slash may be escaped as \/ but does not have to be.Escape Sequences
JSON supports these escapes: \" (quote), \\ (backslash), \/ (slash), \b (backspace), \f (form feed), \n (newline), \r (carriage return), \t (tab), and \uXXXX (Unicode code point). Any other backslash sequence is invalid.
{
"quote": "She said \"hello\"",
"backslash": "C:\\Users\\name",
"newline": "line1\nline2",
"tab": "col1\tcol2",
"carriage": "a\rb",
"backspace": "a\bb",
"formfeed": "a\fb",
"slash": "a\/b"
}
// Valid escapes: \" \\ \/ \b \f \n \r \t \uXXXXUnicode Characters
Use \uXXXX for any Unicode code point. Characters above U+FFFF (like many emojis) require a surrogate pair: two \u escapes. Modern parsers also accept the raw UTF-8 character directly in the string—\uXXXX is mainly for safety or when you cannot type the character.
{
"eacute": "Caf\u00e9",
"euro": "Price: \u20ac10",
"emoji": "Smile \ud83d\ude00",
"chinese": "\u4f60\u597d",
"raw": "Cafu00e9 u00e9 \u00e9"
}
// \uXXXX encodes a code point in the Basic Multilingual Plane.
// Surrogate pairs (two \u escapes) encode characters above U+FFFF.
// Modern parsers also accept the raw UTF-8 character directly.Special Characters in Strings
Inside JSON strings, the double quote and backslash must always be escaped (\" and \\). Other special characters (HTML, SQL, regex) follow their own rules but JSON only requires escaping \" and \\ (plus control characters). A literal backslash is written as \\.
{
"html": "<div class=\"x\">Hi</div>",
"sql": "SELECT * FROM t WHERE name='O''Brien'",
"regex": "^\\d{3}-\\d{4}$",
"json": "{ \"k\": \"v\" }",
"cmd": "echo \"hello\""
}
// Backslash is the universal escape character.
// To include a literal backslash, write \\.Multi-line Strings (Workaround)
JSON strings cannot contain literal newlines—you must escape them as \n. For multi-line content, either embed \n in the string, use an array of line strings, or switch to a format like YAML or JSON5 that supports multi-line strings. Literal unescaped newlines are a syntax error.
// JSON does NOT support real multi-line strings.
// The following is INVALID:
// { "text": "line1
// line2" }
// Workaround 1: escape the newline
{ "text": "line1\nline2" }
// Workaround 2: array of lines
{ "lines": ["line1", "line2"] }
// Workaround 3: JSON5 allows continued strings (still single logical line)Common String Pitfalls
Common string errors: using single quotes, leaving trailing commas, forgetting to escape inner double quotes, and embedding literal control characters (tab/newline) instead of their \t/\n escapes. A strict parser rejects all of these.
// WRONG: single quotes
// { 'name': 'Alice' }
// WRONG: trailing comma
// { "a": 1, }
// WRONG: unescaped quote
// { "msg": "He said "hi"" }
// WRONG: literal tab/newline in string
// { "x": "a<TAB>b" }
// RIGHT:
{ "name": "Alice", "msg": "He said \"hi\"" }Numbers
Integer & Float
JSON has a single number type that covers integers and floating-point values. There is no separate integer type at the syntax level—language bindings may map large integers to int, long, float, or decimal depending on value and library.
{
"count": 100,
"negative": -42,
"temperature": -273.15,
"pi": 3.14159,
"zero": 0,
"negativeZero": -0
}
// Integers and floats use the same number type.
// JSON does not distinguish int from float at the syntax level.Scientific Notation
JSON supports scientific notation: a mantissa followed by e or E and an optional signed exponent (6.022e23 = 6.022 * 10^23). Both e and E are accepted. This is essential for very large or very small numbers in scientific data.
{
"avogadro": 6.022e23,
"planck": 6.626e-34,
"speedOfLight": 2.998e8,
"small": 1.5E-10
}
// e or E is allowed (case-insensitive).
// 6.022e23 means 6.022 * 10^23.
// The exponent may be negative.Number Precision
JSON numbers are typically parsed as IEEE 754 doubles, so integers above 2^53-1 (9007199254740991) lose precision in JavaScript. Decimal values like 0.1 cannot be represented exactly. For money or arbitrary-precision integers, encode them as strings and use a Decimal/BigInteger type in your code.
{
"big": 9007199254740993,
"decimal": 0.1,
"sum": 0.1,
"money": 19.99
}
// JavaScript stores numbers as IEEE 754 doubles.
// Integers are safe up to 2^53 - 1 (9007199254740991).
// 0.1 + 0.2 === 0.30000000000000004 (float precision loss).
// For money or big integers, parse as string or use a Decimal type.Special Values (Not Allowed)
Standard JSON does not allow NaN, Infinity, or -Infinity. Workarounds: use null for NaN, encode Infinity as a string, or use a finite sentinel value. Some serializers (Python's json by default, JSON5) emit these as extensions, but the output is non-standard and may be rejected by strict parsers.
// NaN, Infinity, and -Infinity are NOT valid JSON.
// INVALID:
// { "x": NaN }
// { "y": Infinity }
// { "z": -Infinity }
// Common workarounds:
{
"x": null, // represent NaN/null
"y": "Infinity", // encode as a string
"z": 1.7976931348623157e308 // use a finite max
}
// Many serializers (e.g., Python json) accept these as extensions
// but the output is not standard JSON.Number Formats
Valid JSON numbers: optional minus, integer part (no leading zeros except for 0), optional fractional part, optional exponent. Hex (0x), binary (0b), octal, underscores, leading +, leading zeros, and a dot without digits on both sides are all invalid.
// VALID JSON numbers:
42
-7
0
3.14
-0.5
6.022e23
1.5E-10
100.00
// INVALID:
007 // leading zero
.5 // no leading digit
5. // no trailing digit after dot
+5 // leading plus sign
0x1F // hex
0b101 // binary
1_000 // underscoresLarge Numbers & BigInt
For values above 2^53-1 (e.g., Twitter snowflake IDs, 64-bit integers, large balances), encode them as strings to avoid precision loss. If you must use a reviver in JSON.parse to convert numbers to BigInt, remember BigInt cannot be re-serialized by JSON.stringify without a replacer—strings are safer on the wire.
{
"snowflake": "1234567890123456789",
"timestamp": 1696000000000,
"balance": "99999999999999999999"
}
// Numbers above 2^53-1 lose precision in JS.
// Use STRINGS for IDs, snowflakes, and big integers.
// In JavaScript, parse with a reviver:
// JSON.parse(str, (k, v) =>
// typeof v === "number" && v > Number.MAX_SAFE_INTEGER
// ? BigInt(v) : v); // careful: BigInt can't be in JSON.stringify
// Better: keep them as strings on the wire.Booleans & Null
Boolean Values
JSON booleans are the bare keywords true and false. Quoted 'true'/'false' are strings. JSON has no concept of truthy/falsy at the syntax level—1, 0, yes, no, on, off are all strings or numbers, not booleans. Convert them in application code.
{
"enabled": true,
"disabled": false,
"isPublic": true,
"isActive": false
}
// Only two boolean values: true and false (lowercase, unquoted).
// "true" (quoted) is a STRING, not a boolean.
// 1/0, yes/no, on/off are NOT booleans.Null Meaning
JSON null signals the intentional absence of a value. It is distinct from a missing key (key not in the object), an empty string '', 0, false, or an empty collection. Use null when a field exists but has no value; omit the key when the field itself does not apply.
{
"middleName": null,
"deletedAt": null,
"parent": null,
"optional": null
}
// null means "no value" or "intentionally absent".
// Distinct from:
// - missing key (key not present at all)
// - empty string ""
// - 0 or false
// - empty array [] or object {}Null vs Empty vs Missing
Three distinct states: a key with an empty value (""), a key explicitly null, and a missing key. In JavaScript, undefined means the key is absent while null means it is present but has no value. APIs should document which convention they use for optional fields.
{
"name": "Alice",
"nickname": "", // empty string (present, empty)
"middleName": null, // explicit null (present, no value)
// "maidenName" // missing (key not in object)
}
// In JavaScript:
// obj.nickname === "" // true
// obj.middleName === null // true
// obj.maidenName === undefined // true (key absent)
// "maidenName" in obj // false
// Choose deliberately: null vs "" vs missing.Using Booleans in Conditions
When consuming JSON booleans, prefer strict comparison (=== true) over truthy checks if you need to distinguish false from missing. In API responses, always include boolean fields even when false—omitting them forces clients to guess the default.
{
"isAdmin": true,
"canEdit": false,
"active": true
}
// In application code (JavaScript):
// if (user.isAdmin) { ... } // truthy check
// const canEdit = user.canEdit === true; // strict boolean check
// Be careful: a missing 'canEdit' key is undefined (falsy),
// but explicit false is also falsy. Distinguish if needed.
// API design: always send the boolean, even when false.Common Pitfalls
Common pitfalls: quoting true/false/null turns them into strings; using True/TRUE/None (Python) or true/false with wrong case is invalid; JSON has no undefined (only null). The only valid forms are the lowercase unquoted keywords true, false, and null.
// Pitfall 1: quoting booleans/null
// { "x": "true" } -> x is a STRING, not a boolean
// { "y": "null" } -> y is a STRING, not null
// Pitfall 2: wrong case
// { "x": True } -> INVALID (Python-style, not JSON)
// { "x": TRUE } -> INVALID
// { "x": None } -> INVALID
// Pitfall 3: confusing null with undefined
// null exists in JSON; undefined does NOT.
// Correct:
{ "x": true, "y": null }Arrays
Array Basics
JSON arrays are ordered, zero-indexed lists wrapped in [ ], with comma-separated values. Empty arrays and single-element arrays are valid. Order is significant and preserved by parsers. There is no trailing comma allowed.
{
"numbers": [1, 2, 3, 4, 5],
"strings": ["a", "b", "c"],
"booleans": [true, false, true],
"empty": [],
"single": [42]
}
// Arrays are ordered, zero-indexed lists in [ ].
// Elements are separated by commas (no trailing comma).
// Empty arrays [] and single-element arrays are valid.Mixed Type Arrays
JSON arrays can legally hold mixed types ([1, 'two', true, null]). This is valid but often indicates weak schema design. Homogeneous arrays (all strings, all objects) are easier to validate, document, and consume. Use a schema to enforce element types.
{
"mixed": [1, "two", true, null, 3.14],
"nested": [[1, 2], "x", { "a": 1 }]
}
// JSON arrays CAN hold values of different types.
// This is valid but often a sign of weak schema design.
// Prefer homogeneous arrays for clarity and validation.Nested Arrays
Arrays can nest to any depth, useful for matrices, grids, and tree structures. JSON does not enforce that nested arrays have equal length (a 'ragged' array is valid). If you need a rectangular matrix, enforce it with a schema or application code.
{
"matrix": [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
],
"ragged": [[1, 2], [3, 4, 5], [6]]
}
// Arrays can nest arbitrarily deep.
// JSON does not enforce rectangular shape—use a schema if needed.Array of Objects
An array of objects is the most common pattern for lists of records (users, products, posts). Each element typically has the same shape, but JSON does not enforce this—use a schema (JSON Schema 'items') to validate that all elements conform.
{
"users": [
{ "id": 1, "name": "Alice" },
{ "id": 2, "name": "Bob" },
{ "id": 3, "name": "Carol" }
],
"empty": []
}
// A very common pattern: a list of records.
// Each element is an object with the same shape.Empty and Single-element Arrays
Empty arrays [] and single-element arrays are valid. In API design, distinguish [] (present, empty list) from null (present, no list) and a missing key (field not applicable). Returning [] for 'no items' is usually friendlier than null.
{
"empty": [],
"single": [42],
"tags": [],
"items": ["one"]
}
// Both forms are valid.
// Distinguish [] from null and from a missing key:
// "tags": [] -> present, empty list
// "tags": null -> present, no list
// // (no tags) -> missing keyArray Access in Code
After parsing, JSON arrays become native arrays/lists in your language (JS Array, Python list, Java List, etc.). Use standard index access, length, and iteration. Bounds and type safety are your responsibility—JSON itself does not enforce element types.
{
"list": ["a", "b", "c"]
}
// JavaScript:
// const arr = JSON.parse(json).list;
// arr[0] // "a"
// arr.length // 3
// arr.includes("b") // true
// arr.map(x => x.toUpperCase()) // ["A", "B", "C"]
// Python:
// data = json.loads(json_str)
// data["list"][0] # "a"
// len(data["list"]) # 3Objects
Object Basics
JSON objects are unordered collections of key/value pairs wrapped in { }. Keys must be double-quoted strings; values can be any JSON type. Pairs are comma-separated with no trailing comma. Objects are the most common top-level JSON structure.
{
"id": 1,
"name": "Alice",
"email": "[email protected]",
"active": true
}
// Objects are unordered { } collections of key/value pairs.
// Keys MUST be double-quoted strings.
// Pairs are comma-separated (no trailing comma).Key Naming Rules
JSON keys can be any double-quoted string, including empty, with spaces, dots, or digits. However, for API clarity, prefer consistent camelCase, snake_case, or kebab-case. Avoid spaces, dots (conflict with path syntax), and empty keys in public APIs.
{
"simple": "ok",
"with-dash": "ok",
"with_underscore": "ok",
"with.dot": "ok",
"with space": "ok (but avoid)",
"123numeric": "ok (but avoid)",
"": "empty key is valid"
}
// Keys are arbitrary strings (double-quoted).
// Convention: use camelCase, snake_case, or kebab-case consistently.
// Avoid empty keys and keys with spaces/dots in APIs.Nested Objects
Objects can nest to any depth, useful for hierarchical data. Deep nesting is valid but can hurt readability and increase coupling. For public APIs, prefer a flatter structure or use references (IDs) instead of deeply nested trees.
{
"user": {
"name": "Alice",
"address": {
"street": "123 Main St",
"city": "NYC",
"geo": { "lat": 40.7, "lng": -74.0 }
}
}
}
// Objects can nest arbitrarily deep.
// Deep nesting is valid but can hurt readability—flatten when sensible.Object with Arrays
Combining objects (for keyed fields) and arrays (for ordered lists) models almost any real-world data. A common pattern is an object wrapping arrays of nested objects (a user with orders). Use a schema to document and validate the expected shape.
{
"user": {
"name": "Alice",
"roles": ["admin", "user"],
"orders": [
{ "id": 1, "total": 19.99 },
{ "id": 2, "total": 5.99 }
]
}
}
// Combining objects and arrays models most real-world data.Member Order
The JSON spec treats objects as unordered. In practice, most modern parsers (JavaScript V8, Python 3.7+, Java LinkedHashMap) preserve insertion order, but you should not rely on this for correctness. If order matters for hashing or signing, explicitly sort keys.
{
"z": 1,
"a": 2,
"m": 3
}
// JSON objects are SPECIFIED as unordered.
// In practice, most parsers (JS, Python 3.7+) preserve insertion order.
// DO NOT rely on order for correctness—sort keys if order matters.
// Sorting keys (e.g., for stable hashing):
// JSON.stringify(obj, Object.keys(obj).sort())Duplicate Keys
The JSON spec does not forbid duplicate keys, but their behavior is undefined—most parsers keep the last value, some keep the first. Duplicate keys are a common source of subtle bugs. JSON Schema can enforce uniqueKeys; linters and strict parsers can reject them.
// Technically valid JSON, but behavior is UNDEFINED:
{
"name": "Alice",
"name": "Bob"
}
// Most parsers keep the LAST value ("Bob"),
// but some keep the first. Avoid duplicate keys!
// JSON Schema (draft 2019-09+) has 'uniqueKeys' to forbid them.
// JSON.parse in JS keeps the last value (object property override).Nested Structures
Deep Nesting
JSON allows arbitrary nesting depth, but very deep structures hurt readability and can hit parser limits (many impose a depth cap, e.g., 1000, to prevent denial-of-service). Prefer flatter structures with IDs/references for deeply hierarchical data.
{
"a": {
"b": {
"c": {
"d": {
"e": { "value": 42 }
}
}
}
}
}
// Nesting depth is theoretically unlimited.
// Many parsers impose a limit (e.g., 1000) to prevent DoS.
// Deep nesting hurts readability—consider flattening or references.Real-world Example
Real-world JSON mixes objects and arrays to model entities and their relationships. This order example nests customer, items (an array of objects), and shipping. Use a schema to document the shape; keep nesting moderate for readability.
{
"order": {
"id": "ord-123",
"customer": {
"id": "cus-1",
"name": "Alice",
"email": "[email protected]"
},
"items": [
{ "sku": "A1", "qty": 2, "price": 9.99 },
{ "sku": "B2", "qty": 1, "price": 19.99 }
],
"shipping": {
"address": { "city": "NYC", "zip": "10001" },
"method": "express"
},
"total": 39.97
}
}Tree Structures
Trees are modeled recursively: each node has a value and an array of child nodes. This pattern appears in file systems, org charts, and ASTs. Watch for cycles—JSON has no reference syntax, so cyclic graphs must use IDs or be serialized as a flat list with parent pointers.
{
"name": "root",
"children": [
{
"name": "child-a",
"children": [
{ "name": "grandchild-1", "children": [] },
{ "name": "grandchild-2", "children": [] }
]
},
{ "name": "child-b", "children": [] }
]
}
// Recursive structures: a node contains an array of child nodes.Accessing Nested Values
Accessing deeply nested values risks TypeError/KeyError if intermediate keys are missing. Use optional chaining (JS ?. and ??) or chained .get() (Python) with defaults. Libraries like lodash.get or jq simplify deep extraction and provide defaults.
{
"user": { "address": { "city": "NYC" } }
}
// JavaScript (optional chaining):
// const city = data?.user?.address?.city ?? "unknown";
// Python:
// city = data.get("user", {}).get("address", {}).get("city", "unknown")
// Deep access without guards throws on missing keys.
// Use optional chaining or a helper like lodash.get.Flattening Nested Data
Flattening nested objects (e.g., 'user.name' instead of { user: { name } }) simplifies access for environment variables, form fields, and search indexing. The trade-off is loss of structure and harder round-tripping. Use a library (flat, lodash) to flatten/unflatten.
// Nested form:
{ "user": { "name": "Alice", "city": "NYC" } }
// Flattened form (dot notation):
{ "user.name": "Alice", "user.city": "NYC" }
// Flattened form (separate keys):
{ "userName": "Alice", "userCity": "NYC" }
// Use cases: environment variables, form fields, search indexing.
// Trade-off: simpler access vs. loss of structure.JSON Schema
Schema Basics
JSON Schema is a JSON vocabulary for describing and validating JSON documents. The root declares $schema (draft version) and type. Properties lists fields with their types; required lists mandatory keys. Validators like Ajv (JS) or jsonschema (Python) check conformance.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://example.com/user.json",
"title": "User",
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer", "minimum": 0 }
},
"required": ["name"]
}Type Validation
The 'type' keyword validates a value's JSON type: string, number, integer, boolean, null, array, or object. It can be a single type or an array (e.g., ['string', 'null'] for an optional nullable string). 'integer' matches numbers with no fractional part.
{
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" },
"active": { "type": "boolean" },
"score": { "type": "number" },
"tags": { "type": "array", "items": { "type": "string" } },
"meta": { "type": "object" },
"middle": { "type": ["string", "null"] }
}
}
// 'type' can be a single type or an array of allowed types.String Constraints
String constraints: minLength, maxLength, pattern (regex), enum (allowed values), and format (semantic hint like 'email', 'uri', 'date-time', 'ipv4'). 'format' is annotation-only by default—validators must opt in to enforce it. Use pattern for strict regex validation.
{
"type": "object",
"properties": {
"email": {
"type": "string",
"format": "email",
"maxLength": 254
},
"username": {
"type": "string",
"pattern": "^[a-zA-Z0-9_]{3,20}$",
"minLength": 3,
"maxLength": 20
},
"bio": { "type": "string", "maxLength": 500 }
}
}
// 'format' is a semantic hint (email, uri, date, ipv4).
// 'pattern' is a regular expression.Number Constraints
Number constraints: minimum, maximum (inclusive), exclusiveMinimum, exclusiveMaximum, and multipleOf. For 'integer' type, values must have no fractional part. Combine constraints to model ranges (e.g., a rating from 1 to 5).
{
"type": "object",
"properties": {
"age": { "type": "integer", "minimum": 0, "maximum": 150 },
"price": { "type": "number", "minimum": 0, "exclusiveMinimum": 0 },
"rating":{ "type": "integer", "minimum": 1, "maximum": 5 },
"count": { "type": "integer", "multipleOf": 5 }
}
}
// minimum/maximum are inclusive;
// exclusiveMinimum/exclusiveMaximum are exclusive.Object & Array Constraints
Array constraints: items (element schema), minItems, maxItems, uniqueItems. Object constraints: required (mandatory keys), properties (field schemas), additionalProperties (false to forbid extra keys, or a schema for them), minProperties, maxProperties.
{
"type": "object",
"properties": {
"id": { "type": "integer" },
"tags": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"maxItems": 10,
"uniqueItems": true
}
},
"required": ["id"],
"additionalProperties": false
}
// minItems/maxItems/uniqueItems constrain arrays.
// required lists mandatory keys; additionalProperties
// controls whether extra keys are allowed.Advanced Validation
Advanced JSON Schema: oneOf/anyOf/allOf combine subschemas; $ref references reusable definitions; if/then/else applies conditional validation; const requires an exact value; enum lists allowed values. These let you express rich constraints and reusable types.
{
"oneOf": [
{ "type": "string" },
{ "type": "integer" }
],
"definitions": {
"address": {
"type": "object",
"properties": { "city": { "type": "string" } }
}
},
"properties": {
"home": { "$ref": "#/definitions/address" },
"work": { "$ref": "#/definitions/address" }
},
"if": { "properties": { "type": { "const": "vip" } } },
"then": { "required": ["discount"] }
}
// oneOf/anyOf/allOf combine schemas.
// $ref reuses definitions; if/then/else is conditional.JSONPath
JSONPath Syntax
JSONPath is a query language for JSON, inspired by XPath. '$' is the root, '.' selects a child, '[index]' selects an array element, '*' is a wildcard, and '..' is recursive descent. Libraries like jsonpath-plus (JS) or jsonpath-ng (Python) implement it.
// JSONPath queries JSON like XPath queries XML.
// Sample data:
{
"store": {
"book": [
{ "category": "fiction", "price": 12.99 },
{ "category": "web", "price": 29.99 }
]
}
}
// Common JSONPath expressions:
$.store.book[0].category // "fiction"
$.store.book[*].price // [12.99, 29.99]
$..price // all prices anywhere
$.store..category // all categories under storeRoot & Child
'$' references the root. '.' selects a child by name; '[]' is the bracket alternative that handles keys with dots, spaces, or quotes. '[index]' selects an array element (0-based); some implementations support negative indices for counting from the end.
// '$' = root object/array
// '.' or '[]' = child
$.user.name // dot notation
$['user']['name'] // bracket notation (use for special chars)
$.users[0] // first element of users array
$.users[-1] // last element (some implementations)
// Use bracket notation when keys contain dots, spaces, or quotes:
$['user.name']
$['first name']Recursive Descent
'..' (recursive descent) matches a key or element at any depth. '$..price' returns every 'price' value no matter where it appears. '$..*' returns every node. This is invaluable for searching large or deeply nested JSON when you do not know the exact path.
{
"a": { "b": { "c": 1 }, "c": 2 }
}
// '..' searches at all depths:
$..c // [1, 2] (all 'c' keys anywhere)
$..a // all 'a' keys
$..* // every node (everything)
// Useful for finding a key without knowing the exact path:
$..price // every price, no matter how nestedFilter Expressions
Filters use [?(expression)] where '@' refers to the current item. Supported operators vary by implementation but typically include ==, !=, <, <=, >, >=, && (and), || (or), and sometimes =~ for regex. Functions like length() and contains() add power.
// Filter with [?(expression)]:
$.store.book[?(@.price < 20)] // books under 20
$.store.book[?(@.category == 'web')] // web books
$.store.book[?(@.price < 30 && @.category == 'web')]
// '@' refers to the current item being filtered.
// Operators: == != < <= > >= && || =~ (regex in some impls)
// Functions: length(), contains(), matches()Wildcards
'*' is a wildcard matching all children of an object or all elements of an array. '$.users[*].name' returns the 'name' of every user. '$..*' returns every node. Results are returned as an array of matches, even if there is only one.
{
"users": [
{ "name": "Alice", "age": 30 },
{ "name": "Bob", "age": 25 }
]
}
// '*' matches all elements/children:
$.users[*] // all user objects
$.users[*].name // ["Alice", "Bob"]
$.users[0].* // all values of first user: ["Alice", 30]
$..* // every node in the document
// Wildcards return arrays of matches.Common Functions & Examples
JSONPath functions vary by implementation: length(), min(), max(), avg(), sum(), contains(), and matches() are common. Combine filters and recursive descent for powerful queries. For CLI work, jq is the de facto standard and uses a slightly different syntax.
// length(): array length or string length
$.users.length() // number of users
$.store.book[?(@.title.length() > 10)]
// min(), max(), avg(), sum() (some implementations)
$.store.book..price.min()
// Realistic query: titles of cheap web books
$.store.book[?(@.price < 20 && @.category == 'web')].title
// Tools: jq (CLI), jsonpath-plus (JS), jsonpath-ng (Python),
// Jayway (Java), GoJSONQ (Go)JSON vs XML & YAML
JSON vs XML Syntax
JSON is lighter (no closing tags, native types for numbers/booleans/null) and maps directly to most language data structures. XML is heavier but richer: attributes, namespaces, mixed content, schemas (XSD), and transforms (XSLT). JSON dominates web APIs; XML persists in enterprise/SOAP.
// JSON:
{
"name": "Alice",
"age": 30,
"hobbies": ["reading", "coding"]
}
<!-- XML: -->
<person>
<name>Alice</name>
<age>30</age>
<hobbies>
<hobby>reading</hobby>
<hobby>coding</hobby>
</hobbies>
</person>
// JSON: lighter, no closing tags, types (number/boolean/null).
// XML: heavier, supports attributes, namespaces, mixed content.JSON vs YAML Syntax
YAML uses indentation instead of braces/quotes, supports comments, multi-line strings, and anchors/aliases. JSON is stricter (quotes required, no comments) but faster and more ubiquitous. YAML is a JSON superset—every valid JSON file is also valid YAML.
# YAML:
name: Alice
age: 30
hobbies:
- reading
- coding
// JSON (equivalent):
{
"name": "Alice",
"age": 30,
"hobbies": ["reading", "coding"]
}
// YAML: no quotes/braces, indentation-based, supports comments.
// JSON: stricter, faster to parse, ubiquitous in APIs.JSON to YAML Conversion
Converting JSON to YAML is straightforward because YAML is a JSON superset. Python's yaml.dump and Node's js-yaml handle it. The result uses indentation and no quotes (mostly). YAML's comments and multi-line strings do not round-trip from JSON (JSON has none).
// JSON input:
{ "name": "Alice", "roles": ["admin", "user"] }
// YAML output:
name: Alice
roles:
- admin
- user
// Python:
// import yaml, json
// yaml.dump(json.loads(json_str), sort_keys=False)
// Node.js:
// const yaml = require('js-yaml');
// yaml.dump(JSON.parse(jsonStr));YAML to JSON Conversion
Converting YAML to JSON works for simple YAML but loses YAML-specific features: comments, anchors/aliases, multi-document streams, tags, and complex keys. Use yaml.safe_load to avoid arbitrary code execution from custom tags. The result is standard JSON.
# YAML input:
server:
host: localhost
port: 8080
debug: true
// JSON output:
{
"server": {
"host": "localhost",
"port": 8080,
"debug": true
}
}
// Python:
// import yaml, json
// json.dumps(yaml.safe_load(yaml_str), indent=2)
// Watch for YAML-specific features: anchors, tags, multi-docs.Data Modeling Differences
JSON models data as objects and arrays—no attributes or namespaces. XML adds attributes, namespaces, mixed content, and rich schemas. YAML adds comments, anchors, and multi-document support. TOML targets config files with tables. Choose based on needs: APIs (JSON), documents (XML), config (YAML/TOML).
// JSON: objects and arrays, no attributes.
// Attribute-style metadata needs a convention (e.g., @_prefix).
// XML: elements + attributes, namespaces, mixed content.
// <book id="b1" category="web"><title>...</title></book>
// YAML: maps, sequences, scalars, anchors, multi-doc.
// Supports comments and multi-line strings natively.
// TOML: tables + key/value, designed for config files.
// Best for configuration; not ideal for nested data.When to Use Each Format
JSON for web APIs and NoSQL storage (universal, fast). XML for enterprise services, document-centric data (SVG, XHTML), and rich schemas. YAML for human-authored config (CI/CD, docker-compose) thanks to comments and readability. TOML for small config files. Match the format to the use case.
// Use JSON when:
// - building web/REST APIs (universal browser support)
// - storing structured data (NoSQL databases)
// - configuration with simple needs (package.json)
// Use XML when:
// - enterprise/SOAP web services
// - documents with mixed content (SVG, XHTML, DocBook)
// - you need schemas, namespaces, or XSLT transforms
// Use YAML when:
// - human-written config (CI/CD, docker-compose)
// - you need comments and multi-line strings
// Use TOML when:
// - small config files (Cargo, pyproject)Parsing in JavaScript
JSON.parse
JSON.parse converts a JSON string into a JavaScript value. It accepts any valid JSON value (object, array, string, number, boolean, null). Invalid JSON throws a SyntaxError. Always wrap JSON.parse in try/catch when handling untrusted input.
// Parse a JSON string into a JavaScript value.
const json = '{"name":"Alice","age":30}';
const obj = JSON.parse(json);
console.log(obj.name); // "Alice"
console.log(obj.age); // 30
// JSON.parse accepts a string only.
// Passing a number/boolean/object throws or coerces.
// JSON.parse("42") === 42 (valid)
// JSON.parse("true") === true (valid)
// JSON.parse("null") === null (valid)
// JSON.parse(undefined) // throws SyntaxErrorJSON.stringify
JSON.stringify converts a JavaScript value to a JSON string. Functions, Symbols, and undefined are omitted (in objects) or turned into null (in arrays). BigInt throws TypeError—convert it to a string first. Use the third argument (space) for pretty-printing.
// Serialize a JavaScript value to a JSON string.
const obj = { name: "Alice", age: 30 };
const json = JSON.stringify(obj);
// '{"name":"Alice","age":30}'
// Pretty-print with 2-space indent:
JSON.stringify(obj, null, 2);
// {
// "name": "Alice",
// "age": 30
// }
// Functions, Symbols, and undefined are omitted.
// BigInt throws; use a replacer or String() first.Reviver Function
The JSON.parse reviver transforms values during parsing. It is called bottom-up for every key/value pair (including the root, with key ''). Return the transformed value, or undefined to omit the property. Useful for date parsing, BigInt, and custom types.
// JSON.parse accepts a 'reviver' to transform values.
const json = '{"date":"2024-01-15","count":"42"}';
const obj = JSON.parse(json, (key, value) => {
if (key === "date") return new Date(value);
if (key === "count") return Number(value);
return value;
});
// obj.date is a Date object; obj.count is a number.
// Reviver is called bottom-up for every key, including the root.Replacer Function
The JSON.stringify replacer filters or transforms values during serialization. As a function, it is called for every key—return undefined to omit a property. As an array, it acts as an allowlist of keys to include. Useful for redacting secrets and selecting fields.
// JSON.stringify accepts a 'replacer' to filter/transform.
const obj = { name: "Alice", password: "secret", age: 30 };
// Replacer as a function:
const safe = JSON.stringify(obj, (key, value) => {
if (key === "password") return undefined; // omit
return value;
});
// '{"name":"Alice","age":30}'
// Replacer as an array (allowlist of keys):
JSON.stringify(obj, ["name", "age"]);
// '{"name":"Alice","age":30}'Pretty Printing
Pass a space argument to JSON.stringify for pretty-printing: a number (indent depth) or a string (e.g., '\t'). For stable key order (useful for hashing or signing), sort keys before stringifying. Combine with a replacer to filter fields while pretty-printing.
const obj = { name: "Alice", age: 30 };
// Compact (default):
JSON.stringify(obj);
// '{"name":"Alice","age":30}'
// 2-space indent:
JSON.stringify(obj, null, 2);
// Tab indent:
JSON.stringify(obj, null, "\t");
// Combine replacer and indent:
JSON.stringify(obj, ["name", "age"], 2);
// To insert a stable key order, sort keys first:
const sorted = Object.keys(obj).sort()
.reduce((acc, k) => (acc[k] = obj[k], acc), {});
JSON.stringify(sorted, null, 2);Error Handling
JSON.parse throws SyntaxError on invalid input—always wrap it in try/catch for untrusted data. Common causes: trailing commas, single quotes, comments, or truncated input. For very large or deeply nested JSON, consider a streaming parser or depth limit to prevent denial-of-service.
// Always wrap JSON.parse in try/catch for untrusted input.
function safeParse(str) {
try {
return { ok: true, value: JSON.parse(str) };
} catch (e) {
return { ok: false, error: e.message };
}
}
// Common errors:
// SyntaxError: Unexpected token
// SyntaxError: Unexpected end of JSON input
// (no undefined/NaN/Infinity accepted)
// For deeply nested untrusted JSON, limit depth to avoid DoS.
// Consider a streaming parser for very large files.Parsing in Python
json.loads
json.loads parses a JSON string into Python objects. JSON objects become dicts, arrays become lists, strings become str, numbers become int or float automatically, true/false become True/False, and null becomes None. 'loads' stands for 'load string'.
import json
json_str = '{"name": "Alice", "age": 30}'
data = json.loads(json_str)
print(data["name"]) # Alice
print(data["age"]) # 30
# loads = "load string"
# Returns Python objects: dict, list, str, int/float, bool, None
# JSON true/false/null -> Python True/False/None
# JSON numbers -> int or float (auto)json.dumps
json.dumps serializes a Python object to a JSON string. Use indent for pretty-printing, ensure_ascii=False to keep Unicode characters (instead of \uXXXX escapes), and sort_keys=True for stable output. Python True/False/None map to JSON true/false/null.
import json
data = {"name": "Alice", "age": 30, "active": True}
# Compact:
json.dumps(data)
# '{"name": "Alice", "age": 30, "active": true}'
# Pretty-printed:
json.dumps(data, indent=2, ensure_ascii=False)
# {
# "name": "Alice",
# "age": 30,
# "active": true
# }
# ensure_ascii=False keeps Unicode characters readable.
# sort_keys=True produces stable output.Parsing Files
Use json.load (file object) and json.dump (file object) for files; json.loads and json.dumps for strings. Always open files with encoding='utf-8' to avoid platform defaults. For large files, consider ijson (streaming) or orjson (faster) for better performance.
import json
# Read from a file:
with open("data.json", "r", encoding="utf-8") as f:
data = json.load(f)
# Write to a file:
with open("out.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
# load/dump work on file objects; loads/dumps work on strings.
# Always specify encoding="utf-8" for portability.Custom Encoders
Subclass json.JSONEncoder and override default() to serialize types JSON does not support natively (datetime, set, Decimal, custom classes). Return a JSON-serializable representation. For decoding, write a custom hook (object_hook) to reverse the transformation.
import json
from datetime import datetime, date
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
if isinstance(obj, set):
return sorted(obj)
return super().default(obj)
data = {"now": datetime.now(), "tags": {"a", "b"}}
json.dumps(data, cls=CustomEncoder)
# '{"now": "2024-...", "tags": ["a", "b"]}'
# The default() method is called for objects json can't serialize.Error Handling
json.loads raises json.JSONDecodeError on invalid input, with line/col info. Common causes: trailing commas, single quotes, comments, NaN/Infinity (rejected by default), and control characters in strings. Use parse_constant to handle NaN/Infinity/-Infinity if needed.
import json
def safe_loads(s):
try:
return {"ok": True, "value": json.loads(s)}
except json.JSONDecodeError as e:
return {"ok": False, "error": str(e), "line": e.lineno, "col": e.colno}
# json.JSONDecodeError gives line and column of the error.
# Common causes: trailing commas, single quotes, comments,
# NaN/Infinity (rejected by default), control chars in strings.
# To allow NaN/Infinity (non-standard):
json.loads('{"x": NaN}', parse_constant=lambda x: None) # or float(x)Working with Decimal & DateTime
By default, json parses all numbers as float, losing precision for decimals like money. Pass parse_float=Decimal to keep precision, and serialize with a default hook that converts Decimal back (to str to preserve precision, or float if loss is acceptable). Use str for exact money representation.
import json
from decimal import Decimal
from datetime import datetime
# Decimal: parse_float keeps precision (default would lose it)
data = json.loads('{"price": 19.99}', parse_float=Decimal)
print(data["price"]) # Decimal('19.99')
print(data["price"] + Decimal("0.01")) # 20.00 exact
# Serialize Decimal back:
def encode(obj):
if isinstance(obj, Decimal):
return float(obj) # or str(obj) to keep precision
raise TypeError
json.dumps({"price": Decimal("19.99")}, default=encode)
# parse_float=float is the default; use Decimal for money.Parsing in Java
Jackson Basics
Jackson is the most popular Java JSON library. ObjectMapper.readValue parses JSON into POJOs; writeValueAsString serializes them. Add jackson-databind (plus jackson-core and jackson-annotations) as dependencies. Jackson is fast, mature, and integrates with Spring Boot by default.
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
// Parse JSON to a Java object:
String json = "{\"name\":\"Alice\",\"age\":30}";
User user = mapper.readValue(json, User.class);
// Serialize a Java object:
String out = mapper.writeValueAsString(user);
// Pretty-print:
String pretty = mapper.writerWithDefaultPrettyPrinter()
.writeValueAsString(user);
// Jackson is the de facto JSON library for Java.
// Add: com.fasterxml.jackson.core:jackson-databindGson Basics
Gson is Google's JSON library, simpler than Jackson for basic use. Gson.fromJson parses; toJson serializes. Use GsonBuilder for options (pretty printing, null serialization, custom adapters). Add com.google.code.gson:gson as a dependency. Gson is common in Android.
import com.google.gson.Gson;
Gson gson = new Gson();
// Parse:
User user = gson.fromJson(json, User.class);
// Serialize:
String out = gson.toJson(user);
// Pretty-print:
Gson pretty = new GsonBuilder().setPrettyPrinting().create();
String s = pretty.toJson(user);
// Gson (by Google) is simpler than Jackson for basic use.
// Add: com.google.code.gson:gsonJsonNode Tree Model
Jackson's JsonNode is a tree model for navigating JSON without a POJO. Use get() (returns null if missing) or path() (returns a safe 'missing node' for chaining). asText/asInt/asBoolean have overloads with a default value. Useful when the schema is unknown or varies.
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
ObjectMapper mapper = new ObjectMapper();
JsonNode root = mapper.readTree(json);
String name = root.get("name").asText(); // "Alice"
int age = root.get("age").asInt(); // 30
JsonNode hobbies = root.get("hobbies"); // array
for (JsonNode h : hobbies) {
System.out.println(h.asText());
}
// get() returns null for missing keys; path() returns a
// 'missing node' that you can chain safely.
String city = root.path("address").path("city").asText("unknown");Custom Deserialization
Custom JsonDeserializer handles non-standard JSON encodings (e.g., '$19.99' for money). Annotate a field with @JsonDeserialize(using = ...) or register a SimpleModule for a type. Use JsonSerializer for custom serialization. This keeps your POJOs clean of parsing logic.
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.core.*;
import java.io.IOException;
public class MoneyDeserializer extends JsonDeserializer<Double> {
@Override
public Double deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
String s = p.getText(); // e.g. "$19.99"
return Double.parseDouble(s.replaceAll("[^0-9.]", ""));
}
}
// Annotate the field:
// @JsonDeserialize(using = MoneyDeserializer.class)
// public double price;
// Or register a module for a type.Streaming API
Jackson's streaming API (JsonParser/JsonGenerator) reads and writes JSON token-by-token with constant memory—ideal for huge files. You control the parsing loop and can skip subtrees with skipChildren(). Pair with the tree or POJO model for the parts you actually need.
import com.fasterxml.jackson.core.*;
// Jackson streaming: low memory, fast, for huge files.
JsonFactory f = new JsonFactory();
try (JsonParser p = f.createParser(new File("huge.json"))) {
while (p.nextToken() != JsonToken.END_OBJECT) {
String field = p.getCurrentName();
if ("name".equals(field)) {
p.nextToken();
System.out.println(p.getText());
}
// skip complex values you don't need:
// p.skipChildren();
}
}
// Use streaming for files that won't fit in memory.Parsing in PHP
json_encode
json_encode serializes a PHP value to JSON. PHP arrays become JSON objects (associative) or arrays (sequential). Common flags: JSON_PRETTY_PRINT, JSON_UNESCAPED_UNICODE (keep readable Unicode), JSON_UNESCAPED_SLASHES (don't escape /). Combine flags with the | operator.
<?php
$data = ["name" => "Alice", "age" => 30, "active" => true];
// Compact (default):
$json = json_encode($data);
// {"name":"Alice","age":30,"active":true}
// Pretty-print:
$pretty = json_encode($data, JSON_PRETTY_PRINT);
// Keep Unicode readable (don't escape to \uXXXX):
$unicode = json_encode($data, JSON_UNESCAPED_UNICODE);
// Combine flags with |:
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
| JSON_UNESCAPED_SLASHES);json_decode
json_decode parses JSON into PHP. The second argument controls the result: true returns associative arrays, false (default) returns stdClass objects. Choose based on your access style ($arr['key'] vs $obj->key). PHP arrays with non-sequential keys become JSON objects.
<?php
$json = '{"name":"Alice","age":30,"hobbies":["reading"]}';
// Default: returns associative array OR object (stdClass)
$data = json_decode($json);
echo $data->name; // Alice
// Force associative array:
$arr = json_decode($json, true);
echo $arr["name"]; // Alice
// The second argument (assoc) controls array vs object.
// PHP objects become stdClass instances by default.Error Handling
PHP's json_decode does not throw by default—check json_last_error() and json_last_error_msg() after parsing. Pass JSON_THROW_ON_ERROR (PHP 7.3+) to make it throw JsonException instead, which is cleaner. Common errors: trailing commas, single quotes, and invalid UTF-8.
<?php
$json = '{invalid json}';
$data = json_decode($json);
if (json_last_error() !== JSON_ERROR_NONE) {
echo "Parse error: " . json_last_error_msg();
// e.g., "Syntax error"
}
// PHP does not throw on JSON errors; check json_last_error().
// Common errors: trailing comma, single quotes, invalid UTF-8.
// To throw exceptions instead:
try {
$data = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
echo $e->getMessage();
}Pretty Print & Flags
Common json_encode flags: JSON_PRETTY_PRINT (indent), JSON_UNESCAPED_UNICODE (keep readable non-ASCII), JSON_UNESCAPED_SLASHES (don't escape /), JSON_NUMERIC_CHECK (convert numeric strings to numbers), JSON_THROW_ON_ERROR (throw on failure). Combine with |.
<?php
$data = ["path" => "C:\\Users", "url" => "https://x.com/"];
// Default escapes / and non-ASCII:
json_encode($data);
// {"path":"C:\\Users","url":"https:\/\/x.com\/"}
// Cleaner output:
json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE
| JSON_PRETTY_PRINT);
// {
// "path": "C:\\Users",
// "url": "https://x.com/"
// }
// JSON_NUMERIC_CHECK converts numeric strings to numbers:
json_encode(["id" => "42"], JSON_NUMERIC_CHECK); // {"id":42}Custom Serialization
Implement JsonSerializable and define jsonSerialize() to control how an object is encoded. This is the cleanest way to redact fields (e.g., omit password), rename keys, or compute derived values during serialization. The method returns any JSON-serializable structure.
<?php
class User implements JsonSerializable {
public $name;
public $password;
public function __construct($name, $password) {
$this->name = $name;
$this->password = $password;
}
public function jsonSerialize(): mixed {
return [
"name" => $this->name,
// omit password for security
];
}
}
echo json_encode(new User("Alice", "secret"));
// {"name":"Alice"}
// JsonSerializable lets the class control its JSON form.JSON:API Specification
Document Structure
JSON:API is a specification for building consistent JSON APIs. A document has a top-level 'jsonapi' version object, a primary 'data' resource, optional 'included' related resources, 'meta' for non-standard info, 'links' for pagination, and 'errors' for failures. The media type is application/vnd.api+json.
{
"jsonapi": { "version": "1.1" },
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API Basics"
},
"relationships": {
"author": { "data": { "type": "people", "id": "9" } }
}
},
"included": [
{ "type": "people", "id": "9", "attributes": { "name": "Jane" } }
]
}
// JSON:API is a convention for building JSON APIs.
// Top-level keys: jsonapi, data, errors, meta, links, included.Resource Objects
A JSON:API resource object has a 'type' (the resource kind) and 'id' (a string identifier), plus 'attributes' (the data) and optional 'relationships' (links to other resources) and 'links' (URLs like self). IDs must be strings. The type and id must not appear in attributes.
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "Hello",
"published": true,
"views": 42
},
"links": {
"self": "/articles/1"
}
}
}
// Every resource has a 'type' (string) and 'id' (string).
// 'attributes' holds data; 'relationships' holds links to others.
// 'id' and 'type' MUST NOT appear inside attributes.Relationships
Relationships link a resource to others via resource identifier objects ({type, id}). to-one relationships use a single identifier; to-many use an array. The 'links' object provides 'self' (the relationship URL) and 'related' (the related resource URL) for navigation.
{
"data": {
"type": "articles",
"id": "1",
"relationships": {
"author": {
"data": { "type": "people", "id": "9" },
"links": {
"self": "/articles/1/relationships/author",
"related": "/articles/1/author"
}
},
"tags": {
"data": [
{ "type": "tags", "id": "1" },
{ "type": "tags", "id": "2" }
]
}
}
}
}
// to-one: data is a single resource identifier.
// to-many: data is an array of resource identifiers.Meta & Links
'meta' carries non-standard information like total counts, rate limits, or warnings. 'links' provides hypermedia navigation: self, first, prev, next, last for pagination, plus related URLs. Use null for a missing link (e.g., prev on the first page).
{
"meta": {
"totalPages": 5,
"currentPage": 1,
"rateLimit": 100
},
"links": {
"self": "/articles?page=1",
"first": "/articles?page=1",
"prev": null,
"next": "/articles?page=2",
"last": "/articles?page=5"
},
"data": [ /* ... */ ]
}
// meta: non-standard metadata (counts, rate limits, etc.).
// links: pagination and resource URLs.Errors
JSON:API error documents have a top-level 'errors' array (and no 'data'). Each error object has optional fields: id, status (HTTP code as string), code (app-specific), title (short summary), detail (human explanation), source (pointer to the bad field via JSON Pointer), and meta.
{
"errors": [
{
"id": "err-123",
"status": "404",
"code": "NOT_FOUND",
"title": "Resource Not Found",
"detail": "Article 99 does not exist.",
"source": { "pointer": "/data/attributes/id" },
"meta": { "requestId": "abc-9" }
}
]
}
// Errors is an array of error objects.
// status: HTTP status string; code: app-specific code.
// source.pointer: JSON Pointer to the offending field.JSON5
JSON5 Features
JSON5 is a superset of JSON designed for human-authored config. It adds comments, unquoted/single-quoted strings, trailing commas, hex/Infinity/NaN numbers, and relaxed number formats. JSON5 is not standard JSON—use a JSON5 parser (json5 npm package) and convert to JSON for interchange.
// JSON5 is a superset of JSON with friendlier syntax.
{
// line comments
/* block comments */
unquotedKey: "ok", // unquoted keys
"with space": "ok", // quoted keys still allowed
singleQuote: 'single', // single-quoted strings
trailing: [1, 2, 3,], // trailing commas
hex: 0x1F, // hexadecimal numbers
inf: Infinity, // Infinity, -Infinity, NaN
leadingDot: .5, // leading decimal point
trailingDot: 5., // trailing decimal point
plus: +5, // leading plus
}
// JSON5 is NOT standard JSON—use a JSON5 parser.Comments
JSON5 supports both // line comments and /* */ block comments, which standard JSON forbids. This makes JSON5 ideal for human-authored config files. VS Code's settings.json and TypeScript's tsconfig.json use JSONC (JSON with Comments), a similar but slightly different subset.
{
// Single-line comment (like JS)
"name": "Alice",
/* Block comment
spanning multiple lines */
"age": 30,
"active": true // end-of-line comment
}
// Standard JSON rejects comments.
// JSON5 supports // and /* */ comments everywhere.
// VS Code's settings.json and tsconfig.json use JSONC (a subset).Trailing Commas
JSON5 allows trailing commas after the last element of objects and arrays. Standard JSON rejects them. Trailing commas reduce diff noise when adding or reordering fields (each line can end with a comma). Many modern languages and config formats now allow this.
{
"a": 1,
"b": 2,
"c": 3, // <-- trailing comma OK in JSON5
}
// Arrays too:
[1, 2, 3,]
// Standard JSON rejects trailing commas.
// JSON5 allows them, reducing diff noise when adding fields.Unquoted & Single-quoted Strings
JSON5 allows unquoted identifier keys (matching [A-Za-z_$][A-Za-z0-9_$]*) and both single and double-quoted strings. Inside a single-quoted string, escape ' as \'; inside a double-quoted, escape " as \". This mirrors JavaScript object literal syntax for readability.
{
unquoted: "value", // unquoted identifier key
"quoted key": "value", // quoted key (for special chars)
single: 'single-quoted', // single-quoted string value
mixed: 'it\'s ok', // escape single quote inside
double: "she said \"hi\"", // escape double quote inside
}
// Unquoted keys must be valid identifiers ([A-Za-z_$][A-Za-z0-9_$]*).
// Single and double quotes are both allowed for strings.Numbers & Strings (Extended)
JSON5 extends numbers: hexadecimal (0xFF), leading/trailing decimal points (.5, 5.), leading plus (+5), and the keywords Infinity, -Infinity, and NaN. These are all rejected by standard JSON. JSON5 strings still use escape sequences (\n) for newlines—there are no bare multi-line strings.
{
hex: 0xFF, // hexadecimal
leadingDot: .5, // .5 instead of 0.5
trailingDot: 5., // 5. instead of 5.0
leadingPlus: +5, // +5 (standard JSON rejects +)
infinity: Infinity, // Infinity (not in standard JSON)
negativeInfinity: -Infinity,
nan: NaN,
multiline: 'line1\nline2' // escape sequences like JSON
}
// JSON5 does NOT support real multi-line strings or bareword NaN.JSON Lines (JSONL)
JSONL Format
JSON Lines (JSONL or NDJSON) is a format where each line is a separate, complete JSON value. There is no enclosing array. This makes it ideal for streaming, logs, and large datasets: you can process line-by-line without loading the whole file. Extensions: .jsonl, .ndjson.
// A JSON Lines file (.jsonl / .ndjson) is one JSON value per line:
{"name": "Alice", "age": 30}
{"name": "Bob", "age": 25}
{"name": "Carol", "age": 35}
// Rules:
// - Each line is a COMPLETE, valid JSON value.
// - Lines are separated by newlines (\n).
// - No enclosing [ ] array.
// - Each line is parsed independently.Reading JSONL
Read JSONL by iterating lines and parsing each with JSON.parse/loads. This streams the file with constant memory—ideal for huge datasets. Skip blank lines. In Node.js, use readline over a file stream; in Python, iterate the file object directly. No need to load the whole file.
// JavaScript (Node.js) — line by line:
const fs = require("fs");
const readline = require("readline");
const rl = readline.createInterface({
input: fs.createReadStream("data.jsonl"),
crlfDelay: Infinity,
});
for await (const line of rl) {
if (!line.trim()) continue;
const obj = JSON.parse(line);
console.log(obj.name);
}
// Python:
// import json
// with open("data.jsonl") as f:
// for line in f:
// obj = json.loads(line)Writing JSONL
Write JSONL by serializing each record with JSON.stringify/dumps (compact, no indent) and appending a newline. Use a writable stream (Node) or open the file in write mode (Python) for efficiency. Compact form keeps lines short and parseable; one record per line is the rule.
// JavaScript (Node.js):
const fs = require("fs");
const out = fs.createWriteStream("out.jsonl");
for (const obj of records) {
out.write(JSON.stringify(obj) + "\n");
}
out.end();
// Python:
// import json
// with open("out.jsonl", "w") as f:
// for obj in records:
// f.write(json.dumps(obj) + "\n")
// One JSON per line; compact form (no indent) is conventional.Use Cases
JSONL shines for log files, large datasets, ML training data, database dumps, and append-only event streams. Each line is independent, so you can append without rewriting, resume parsing after a corrupt line, and process in parallel. Tools like jq and shell pipes work naturally with it.
// JSONL is ideal for:
// - log files (one event per line)
// - large datasets (streaming, append-only)
// - machine learning training data
// - database exports/imports
// - append-only event streams
// Example: log entry per line
{"ts": "2024-01-15T10:00:00Z", "level": "info", "msg": "started"}
{"ts": "2024-01-15T10:00:01Z", "level": "error", "msg": "failed"}
// Tools: jq, xargs, awk | json.loads — all work line-by-line.JSONL vs JSON Array
A JSON array wraps records in [ ] and requires loading (or carefully streaming) the whole file to parse. JSONL puts one record per line, enabling true streaming, easy appending, and recovery from corrupt lines. For large or growing datasets, JSONL is almost always the better choice.
// JSON Array (one big file):
[
{"name": "Alice"},
{"name": "Bob"},
{"name": "Carol"}
]
// JSONL (one per line):
{"name": "Alice"}
{"name": "Bob"}
{"name": "Carol"}
// JSON Array: must load whole file to parse; hard to append.
// JSONL: streamable, appendable, resumable; slightly more
// per-record overhead but far better for big files.Security
JSONP Risks
JSONP wraps JSON in a JavaScript callback so a <script> tag can fetch cross-origin data, bypassing the same-origin policy. This is dangerous: any site can read the response, callback injection enables XSS, and requests carry the victim's cookies. Use CORS instead—JSONP is legacy and unsafe.
// JSONP (JSON with Padding) bypasses same-origin policy
// by wrapping JSON in a script callback:
callbackHandler({"secret": "data"});
// Loaded via <script src="https://api.example.com/data?callback=callbackHandler">
// RISKS:
// - Any site can include the URL and read the response.
// - XSS if the callback name is attacker-controlled.
// - CSRF: the request is made with the victim's cookies.
// MODERN ALTERNATIVE: use CORS instead of JSONP.
// JSONP is legacy—avoid it for new APIs.XSS Prevention
JSON embedded in HTML <script> tags can cause XSS: the string '</script>' ends the script early. Also, U+2028 and U+2029 (line/paragraph separators) are valid in JSON strings but break JavaScript. Escape <, >, &, U+2028, U+2029 when embedding JSON in HTML, or use a library like serialize-javascript.
// JSON itself is data, not executable—BUT embedding it in
// HTML can cause XSS if not careful.
// BAD: output JSON inside <script> without escaping:
// <script>
// var data = {"name": "</script><script>alert(1)</script>"};
// </script>
// GOOD: escape < and > (and &) when embedding in HTML:
const safe = JSON.stringify(data)
.replace(/</g, "\\u003c")
.replace(/>/g, "\\u003e")
.replace(/&/g, "\\u0026")
.replace(/\u2028/g, "\\u2028")
.replace(/\u2029/g, "\\u2029");
// Or use a library: js-xss, serialize-javascript.Prototype Pollution
Prototype pollution occurs when untrusted JSON containing keys like '__proto__' or 'constructor' is merged into objects, poisoning Object.prototype and affecting all objects. Defenses: use Object.create(null), block dangerous keys, prefer Map for untrusted data, and keep merge libraries updated.
// Merging untrusted JSON into objects can pollute prototypes:
const evil = JSON.parse('{"__proto__": {"isAdmin": true}}');
// BAD merge (Object.assign does NOT pollute, but some libs do):
// function merge(target, src) {
// for (const k in src) target[k] = src[k];
// }
// Attackers can set Object.prototype.isAdmin = true,
// affecting ALL objects.
// Defenses:
// - Use Object.create(null) for parsed objects.
// - Block keys like __proto__, constructor, prototype.
// - Use Map instead of plain objects for untrusted data.
// - Keep dependencies updated (some had merge() flaws).Injection via JSON
JSON itself resists injection, but building it via string concatenation is dangerous—user input can break out of the JSON structure. Always use a real serializer (JSON.stringify). The same applies downstream: use parameterized SQL queries, execFile with arg arrays, and HTML-escaping templating engines.
// JSON itself is not injectable, but building JSON by
// concatenation is dangerous:
// BAD: string concatenation
// const json = '{"name": "' + userInput + '"}';
// // userInput = '"); drop table; //' -> breaks out
// GOOD: always use a real serializer
const json = JSON.stringify({ name: userInput });
// For SQL: use parameterized queries, not JSON interpolation.
// For shell: use execFile with arg arrays, not string concat.
// For HTML: use textContent or a templating engine that escapes.DoS via Large or Deep JSON
JSON-based DoS: huge files exhaust RAM, deep nesting overflows the parser stack, and pathological structures (the JSON analog of 'billion laughs') inflate small input. Defenses: cap request body size at the proxy, limit parsing depth, use streaming parsers for large input, and set timeouts.
// Attackers can craft JSON to exhaust memory or CPU:
//
// 1. Huge file: 1 GB of '[1,1,1,...]' to exhaust RAM.
// 2. Deep nesting: '{"a":{"a":{"a":...}}}' (10000 levels)
// to overflow the parser's stack.
// 3. 'Billion laughs': nested entity expansion (XML-era,
// but JSON analogs exist with $ref in some libs).
// 4. Many keys: 10 million keys in one object.
// Defenses:
// - Cap request body size at the server/reverse proxy.
// - Limit parsing depth (Ajv, fast-json-parse options).
// - Use streaming parsers (stream-json, oboe) for big input.
// - Set timeouts on parsing.Safe Parsing Practices
Safe JSON parsing: always use JSON.parse (never eval), validate against a schema (Ajv, jsonschema) before use, cap depth and key count, disable __proto__ keys, and consider parsing untrusted input in a Web Worker or sandbox. JSON.parse itself is safe from code execution; the risks are in how you use the parsed data.
// 1. Validate with a schema before using untrusted JSON.
const Ajv = require("ajv");
const ajv = new Ajv({ allErrors: true });
const validate = ajv.compile(schema);
if (!validate(data)) throw new Error(ajv.errorsText(validate.errors));
// 2. Limit depth and key count when parsing.
// 3. Use reviver/replacer to coerce types safely.
// 4. Never eval() or Function('return ' + json) — use JSON.parse.
// 5. Disable dangerous features (e.g., __proto__ keys).
// 6. Parse untrusted JSON in a sandbox or worker if possible.
// JSON.parse is safe; eval is NOT.Best Practices & Tools
Schema First
Design APIs schema-first: define a JSON Schema, generate language types from it (typescript-json-schema, quicktype, jsonschema2pojo), and validate requests/responses at runtime. This catches contract violations early, enables code generation, and serves as living documentation.
// Define a JSON Schema before shipping an API.
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"required": ["id", "name"],
"properties": {
"id": { "type": "integer" },
"name": { "type": "string", "minLength": 1, "maxLength": 100 }
},
"additionalProperties": false
}
// Generate types from the schema (TypeScript, Java, Go, ...).
// Validate both requests and responses against the schema.
// Publish the schema for clients (OpenAPI, JSON Schema).Consistent Naming
Choose one naming convention for keys and apply it consistently: camelCase (most JSON APIs and JS), snake_case (Python/Ruby backends, Google APIs), or PascalCase (.NET). Mixing styles is confusing. If front and back ends differ, convert at a single boundary rather than mixing in the schema.
// Pick ONE naming convention and apply it everywhere.
// camelCase (most JSON APIs, JavaScript ecosystem):
{ "firstName": "Alice", "lastName": "Smith" }
// snake_case (Python/Ruby backends, many Google APIs):
{ "first_name": "Alice", "last_name": "Smith" }
// kebab-case (rare in JSON, common in URLs/config):
{ "first-name": "Alice" }
// PascalCase (rare in JSON keys, common in C#/.NET types):
{ "FirstName": "Alice" }
// Avoid mixing; convert at the boundary if the front/back differ.Versioning
Version APIs from day one: in the URL path (/v1/), a version field in the payload, the Accept header (media type), or a namespace (JSON-LD @context). Additive changes (new optional fields) are usually non-breaking; bump the version for removed/renamed fields or changed semantics.
// 1. Version in the URL path:
// https://api.example.com/v1/users
// 2. Version field in the payload:
{ "version": "1.0", "data": { ... } }
// 3. Accept header / media type:
// Accept: application/vnd.example.v1+json
// 4. Namespace the JSON (JSON-LD, JSON:API 'type'):
{ "@context": "https://schema.org/v1", ... }
// Plan for evolution: add optional fields freely, but bump
// the version for breaking changes (removed/renamed fields,
// changed semantics or types).Encoding (UTF-8)
UTF-8 is the default and only widely-recommended encoding for JSON. Save files as UTF-8 without a BOM (some parsers mishandle it), set Content-Type with charset=utf-8, and set Content-Length in bytes. In Python, pass ensure_ascii=False to keep Unicode characters readable instead of \uXXXX escapes.
// Always use UTF-8 for JSON.
// - Save files as UTF-8 (no BOM).
// - Set Content-Type: application/json; charset=utf-8.
// - Set Content-Length in bytes (not characters).
// In Node.js:
res.setHeader("Content-Type", "application/json; charset=utf-8");
res.end(JSON.stringify(data));
// In Python:
import json
with open("out.json", "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False)
// ensure_ascii=False keeps Unicode readable in the file.Performance Tips
For performance: stream large JSON (ijson, stream-json, Jackson streaming) instead of buffering; use fast serializers (orjson, fast-json-stringify); transmit compact and compress with gzip/brotli over HTTP; avoid deep nesting and huge arrays; cache parsed results to avoid re-parsing.
// 1. Stream large JSON instead of buffering it all.
// - Node: stream-json, JSONStream
// - Python: ijson
// - Java: Jackson streaming API
// 2. Use a fast serializer:
// - Node: fast-json-stringify (schema-based), orjson (Python)
// - Compile serializers from a schema for hot paths.
// 3. Compact JSON for transmission; pretty only for humans.
// 4. Avoid deep nesting and huge arrays when possible.
// 5. Compress with gzip/brotli over HTTP (huge savings).
// 6. Cache parsed results; don't re-parse the same payload.Tools & Validators
Tools for working with JSON: online validators (jsonlint.com, jsonschemavalidator.net), CLI tools (jq for querying/formatting, jsonschema for validation), editor support (VS Code and JetBrains have built-in JSON + schema validation), and code generators (quicktype generates TypeScript/Java/Go/etc. types from JSON or a schema).
// Online validators:
// - https://jsonlint.com
// - https://jsonformatter.org
// - https://www.jsonschemavalidator.net
// CLI tools:
// - jq (filter, query, format JSON on the command line)
// - jaq (Rust jq clone, faster)
// - jsonschema (Python CLI for schema validation)
// - ajv (Node CLI/library for JSON Schema)
// Editor support:
// - VS Code: built-in JSON + schema validation
// - JetBrains: built-in JSON + schema support
// - vim/emacs: plugins available
// Schema tools:
// - quicktype (generate types from JSON)
// - typescript-json-schema
// - jsonschema2pojo (Java)관련 JSON 스니펫
Copy-paste ready code for common tasks.
Data Types
The seven JSON value types.
Nested Objects
Objects within objects for hierarchical data.
Arrays
Lists of mixed and homogeneous values.
Schema Validation
Validate structure with JSON Schema.
JSONPath
Query expressions for locating nodes.
Merge Patch (RFC 7386)
Partially update JSON documents.
JSON Pointer (RFC 6901)
Address a specific value by path.
Streaming (NDJSON)
Newline-delimited JSON for streaming.
Was this helpful?