JSON File Documentation


Summary

A JSON (JavaScript Object Notation) file stores structured, human-readable data as key–value pairs and ordered lists, written in UTF-8 text. It is standardised as ECMA-404 and IETF RFC 8259 and is the dominant format for web APIs, configuration files, and NoSQL storage. Any text editor opens a .json file, so the common questions are about syntax rules — double-quoted keys, no comments, no trailing commas — and how to validate or pretty-print it.

Technical details

Feature Value
File Extension .json - Signifies that the file contains JSON-formatted data.
MIME Type application/json - Standard media type for JSON data.
Standardization ECMA-404, RFC 8259 - JSON is standardized by ECMA International and defined in Internet standards by the IETF.
Primary Use Data Interchange - Commonly used for transmitting structured data over a network.
Human-Readable Yes - JSON is designed for readability and ease of use. The structure is both simple and self-explanatory.
Encoding UTF-8 (required for interchange) - RFC 8259 mandates UTF-8 for JSON exchanged between systems. UTF-16 and UTF-32 were permitted only under the earlier RFC 7159.
Root Element Object or Array - A JSON document can start with either an object (curly braces {}) or an array (square brackets []).
Supported Data Types String, Number, Object, Array, Boolean, Null - JSON supports a limited yet flexible set of data types.
Nesting Support Yes - JSON supports nested objects and arrays, enabling complex data structures.
Comments No - JSON does not natively support comments. You'll have to use workarounds if comments are necessary.
Meta-information Support No - Unlike XML, JSON does not have attributes or metadata elements within the data structure.
Binary Data Support No - JSON doesn't handle binary data natively. Binary data has to be Base64 encoded before including it in a JSON document.
Native Language Support JavaScript - JSON is natively supported in JavaScript but libraries exist for most other programming languages.
Platform-agnostic Yes - JSON is platform-independent and can be used in a variety of programming environments.
Max Depth for Nesting Implementation-dependent - The maximum depth for nesting objects or arrays is dependent on the language/library being used.
Security Features None - JSON itself doesn’t have built-in security features. Security measures must be implemented externally.
Parsing Complexity Low to Moderate - While JSON is easier to parse than XML, it does incur a computational cost, particularly for large files.
Serializability Yes - JSON data can be easily serialized for storage or transmission and later deserialized back into its original structure.
Case Sensitivity Yes - JSON keys are case-sensitive, meaning "Key" and "key" would be considered different keys.
Whitespace Handling Flexible - Whitespace can be included between any pair of tokens, making it more human-readable without affecting data integrity.
Key Quotation Required - Object keys must be wrapped in double quotes, unlike some other data formats that may not require key quotation.
Value Restrictions No NaN/Infinity - JSON does not support NaN (Not-a-Number), Positive Infinity, or Negative Infinity as numeric values.
Order Significance Objects: No, Arrays: Yes - The order of key-value pairs in objects is not significant, while the order of values in arrays is.
Array Uniqueness No - JSON arrays do not require unique values. Duplicates are allowed.
Object Key Uniqueness Yes - In a JSON object, each key must be unique. If duplicate keys exist, the behavior is undefined.
Locale Handling No native support - JSON doesn't have native localization or internationalization features.
Fragment Identifier JSON Pointer - JSON Pointer (RFC 6901) can be used as a fragment identifier to point to a specific piece of JSON data.
Streaming Support Yes - Streaming parsers (for example Jackson Streaming in Java or ijson in Python) read JSON incrementally without holding the whole document in memory; only DOM-style parsers load it all at once.
Database Support Widespread - Many NoSQL databases natively use JSON-like formats, and several relational databases also support JSON fields.
Error Handling Strict - A single error in a JSON file (like a trailing comma) will result in a parsing error.
SEO Support Structured Data - JSON-LD (JSON for Linking Data) is commonly used for structuring metadata in web pages for SEO purposes.
JSON syntax rules at a glance

JSON is plain UTF-8 text with no file signature. A document is a single value — usually an object { } or an array [ ]. Object keys must be double-quoted strings; single quotes are invalid. Values may be a string, number, true, false, null, object, or array. Comments are not allowed, and a trailing comma after the last element is a syntax error. Numbers cannot be NaN or Infinity.

What is a JSON file?

A JSON file holds structured data written in JavaScript Object Notation, a text format that encodes name/value pairs and ordered lists. Douglas Crockford specified it at json.org in 2001, taking the syntax from JavaScript object literals. Despite that origin, JSON is language-independent: every mainstream language ships a parser, so a .json file written by a Python script reads cleanly in Go, Ruby, or C#. The IETF first published it as RFC 4627 in 2006, and in December 2017 two specifications took effect together: ECMA-404 and IETF RFC 8259 (STD 90). RFC 8259 is the normative grammar this article follows.

The rest of this page treats JSON as a grammar rather than a tutorial. JSON is small enough that the entire syntax fits on a single page of the RFC, which is unusual for an interchange format. That grammar is what a parser accepts or rejects, and understanding it explains most of the errors people hit: a trailing comma, a leading zero, a smart-quote pasted in place of a straight quote. Every rule below is drawn directly from the ABNF in RFC 8259.

The value grammar: object, array, string, number, and three literals

RFC 8259 defines a JSON text as a single value, optionally surrounded by whitespace. A value is exactly one of seven productions: an object, an array, a string, a number, or one of the three literal names true, false, and null. Those literals are the only bare words the grammar permits; every other token is a delimiter, a quoted string, or a number. There is no undefined, no date type, no comment production, and no way to reference another value. The whole grammar is closed under those seven rules.

Two of the seven, object and array, are structural: they contain other values and therefore nest to arbitrary depth. That recursion is how JSON represents trees. An array is a comma-separated sequence of values between [ and ]; the order of elements is significant and duplicates are allowed. The empty array [] and empty object {} are both legal values. Note that the top-level text does not have to be an object or array: RFC 8259 removed the RFC 4627 restriction, so a bare 42, true, or "hello" is a complete, valid JSON document. In practice APIs still answer with an object or array so they have room to grow.

Object members: the exact member grammar

An object is a set of members between { and }, members separated by commas. Each member is a string (the name), a single :, and a value. The grammar reads, in the RFC's ABNF form:

object = begin-object [ member *( value-separator member ) ] end-object
member = string name-separator value

begin-object    = ws %x7B ws   ; { left curly bracket
end-object      = ws %x7D ws   ; } right curly bracket
name-separator  = ws %x3A ws   ; : colon
value-separator = ws %x2C ws   ; , comma

Two consequences fall straight out of this. First, a member name must be a string, which means it must be double-quoted: {name: 1} is invalid, {"name": 1} is valid. This is the single most common hand-editing mistake, and it is why JSON is stricter than the JavaScript object literals it was drawn from, where unquoted keys are legal. Second, because member *( value-separator member ) puts the comma between members, a comma after the last member (a trailing comma) has no member to introduce and the parse fails. JSON has no trailing-comma allowance anywhere, in objects or arrays.

String escapes and \uXXXX

A string is a sequence of Unicode characters between double quotes. Most characters stand for themselves, but three cases must be escaped with a backslash: the quotation mark ", the backslash \, and any control character in the range U+0000 to U+001F. RFC 8259 defines exactly two escape mechanisms. The first is a set of two-character shortcuts: \", \\, \/, \b (backspace, U+0008), \f (form feed, U+000C), \n (line feed, U+000A), \r (carriage return, U+000D), and \t (tab, U+0009). The escaped solidus \/ is optional and exists only so that the sequence </ can be broken up when JSON is embedded in an HTML <script> element.

The second mechanism is \u followed by exactly four hexadecimal digits, which names a single UTF-16 code unit: é is é, A is A. Because \uXXXX is a 16-bit unit, any character above U+FFFF (emoji, many CJK extension characters) cannot be written with one escape. It is encoded as a surrogate pair: two \u escapes, a high surrogate in U+D800–U+DBFF followed by a low surrogate in U+DC00–U+DFFF. The musical G-clef U+1D11E, for example, is written 𝄞. A lone or mismatched surrogate is technically ill-formed, and RFC 8259 warns that parsers differ on how they handle it, so it is a real interoperability hazard.

Escapes are optional for anything a character can represent directly. RFC 8259 requires that text exchanged between systems be encoded in UTF-8, and forbids a leading byte-order mark (RFC 4627 was looser). So the accented é can appear literally as its two UTF-8 bytes, or as é; both parse to the same string. Escaping is only mandatory for the quote, the backslash, and the C0 control range.

The number grammar: why 01 is invalid

A JSON number is a decimal in a fixed shape: an optional minus sign, an integer part, an optional fraction, and an optional exponent. The ABNF is precise:

number = [ minus ] int [ frac ] [ exp ]
int    = zero / ( digit1-9 *DIGIT )
frac   = decimal-point 1*DIGIT
exp    = e [ minus / plus ] 1*DIGIT

Read int carefully: the integer part is either a single 0, or a digit 1–9 followed by more digits. There is no production that lets a 0 be followed by another digit, so 01, 007, and -0123 are all syntax errors. The rule exists to remove any ambiguity with octal notation. A leading + is also disallowed (only - is a valid sign), and a number may not end at the decimal point: 1. fails because frac requires at least one digit after the point, and .5 fails because int is not optional. Valid forms include 0, -0, 3.14, 1e10, 2.5E-4.

The grammar deliberately has no token for NaN, Infinity, or -Infinity: those are not numbers in JSON and any serializer that emits them is producing non-conforming output. RFC 8259 also sets no limit on the magnitude or precision of a number, but notes that interoperability is best when values stay within IEEE 754 double-precision range, since that is what most parsers use internally. A number like 1e400 is grammatically valid yet will overflow to infinity in a typical parser.

Insignificant whitespace and duplicate keys

Whitespace in JSON is limited to exactly four characters: space (U+0020), horizontal tab (U+0009), line feed (U+000A), and carriage return (U+000D). No other character, including the Unicode no-break space, counts as JSON whitespace. It is permitted before and after any of the six structural characters ({ } [ ] : ,) and around the top-level value, and it is insignificant: {"a":1} and the same object spread across ten indented lines parse identically. Minifiers exploit this by stripping every optional space, which is why an exported .json often arrives as one long line. Whitespace is not permitted inside a token, so tr ue or 1 . 5 are errors.

Duplicate member names are the grammar's one genuinely underspecified corner. The syntax does not forbid {"a": 1, "a": 2}, and RFC 8259 states that the names within an object SHOULD be unique but does not require it. When a name repeats, the standard explicitly says the behaviour is unpredictable and left to the implementation: many parsers keep the last value (2), some keep the first, some collect all, some raise an error. Because nothing is guaranteed, a document with duplicate keys is interoperable in name only, and security-sensitive parsers sometimes reject it outright to avoid the ambiguity being used to smuggle a different value past a validator.

A worked JSON document using every type

The sample below is a complete, conforming JSON text that exercises all seven value productions: an object at the top, a nested array of objects, strings (one with a \u escape), numbers (integer, negative, and exponent forms), the two booleans, and null.

{
  "name": "John Doe",
  "age": 30,
  "isMarried": false,
  "middleName": null,
  "balance": -12.5e3,
  "note": "café owner",
  "children": [
    { "name": "Alice", "age": 5 },
    { "name": "Bob",   "age": 7 }
  ]
}

Every string is double-quoted, no member ends with a comma, null and false appear as bare literals, and -12.5e3 follows the number grammar exactly (sign, non-zero integer part, fraction, exponent). The é and the literal characters around it are all valid UTF-8 once written to disk.

JSON value to language type mapping

Parsers map the seven JSON productions onto their host language's types. The correspondence is direct for the scalar types and structural for objects and arrays:

JSON value JavaScript Python Go (encoding/json)
object Object dict map[string]interface{} or struct
array Array list []interface{}
string String str string
number Number (IEEE 754 double) int or float float64
true / false Boolean True / False bool
null null None nil

The number row is where round-trips leak. JavaScript has one numeric type (a 64-bit double), so an integer larger than 2^53 loses precision on parse; Python preserves arbitrary integers but folds decimals to floats. A 64-bit database ID sent as a JSON number can therefore change value simply by passing through a browser, which is why many APIs send large integers as strings.

JSON Pointer (RFC 6901): addressing a value inside a document

JSON itself has no fragment-identifier syntax, so RFC 6901 defines JSON Pointer: a string that names one specific value inside a document. A pointer is a sequence of reference tokens, each preceded by /. Given the sample above, /children/0/name resolves to "Alice": children selects the array, 0 selects its first element (arrays are zero-indexed), and name selects that object's member. Because / and ~ are structural in a pointer, a literal ~ in a member name is escaped as ~0 and a literal / as ~1. JSON Pointer is the addressing layer underneath JSON Patch, JSON Schema's $ref, and many API error messages that report exactly which field failed validation.

JSON Lines, JSON-LD, and GeoJSON

Several conventions build on the base grammar without changing it. JSON Lines (also called NDJSON) puts one complete JSON value on each line, separated by \n. The file as a whole is not a single JSON text, so a conforming parser rejects it; the point is that a reader can consume one line at a time and never hold the whole file in memory, which suits logs and multi-gigabyte data exports. GeoJSON (RFC 7946) is ordinary JSON with a fixed schema for geographic data: an object with a "type" of Feature or FeatureCollection and a "geometry" whose coordinates are always ordered longitude, then latitude. JSON-LD (a W3C recommendation) adds a "@context" member that maps plain member names to IRIs, turning a JSON object into linked data; it is the format search engines read for structured data on web pages. All three are valid JSON documents (except JSON Lines, which is a valid JSON value per line) and need no special grammar beyond the conventions on member names.

DOM versus streaming parsing and memory cost

There are two ways to read a JSON document, and the choice is a memory decision. A DOM parser (JavaScript's JSON.parse, Python's json.loads) reads the whole text and builds the entire value tree in memory before returning anything. It is simple and the parsed structure is directly navigable, but peak memory is a multiple of the file size, so a 2 GB export can exhaust a phone or a small container. A streaming (SAX-style) parser instead emits events, start-object, key, value, end-array, as it walks the bytes, and never holds more than the current path. Streaming libraries such as Jackson's JsonParser, Python's ijson, and Go's json.Decoder.Token can process a file far larger than available RAM, at the cost of more code because the application must reassemble the parts it cares about. This is the concrete reason JSON Lines is preferred for large datasets: each line is a small, independent DOM parse, giving streaming behaviour with a plain parser and no streaming API.

JSON, XML, and YAML at the grammar level

The differences with neighbouring formats are grammatical, not stylistic. XML carries attributes, namespaces, comments, and a document-type declaration, none of which JSON has; the trade is verbosity and a more involved parse. YAML takes the opposite direction: it is a strict superset of JSON, so every .json file is already valid YAML, but YAML adds indentation-based structure, comments, anchors, and multiple documents per file, which makes its parser far larger. JSON's whole appeal is that its grammar is small enough to specify completely and implement identically everywhere, so the same seven productions decoded by a browser, a database, and an embedded device produce the same tree.

References