JSON 01 ๐ Introduction to Data Serialization and Formats
Data serialization is the process of converting an in-memory data structure into a format that can be stored or transmitted, and deserialization is the reverse. The format is the contract between the two sides: the writer produces the bytes, and the reader parses them back into the structure. The choice of format is not a matter of taste. It is a trade-off between human readability, machine efficiency, schema enforcement, and ecosystem support. JSON, XML, YAML, and the binary formats each sit at a different point on that trade-off, and the right choice depends on what the data is for.
JSON is the format that won the web. It is a text format with a small grammar, it is natively parseable by JavaScript, and it is the default for the HTTP APIs. The RFC 8259 defines it as a text format derived from JavaScript’s object literal syntax, and it specifies that JSON can represent four primitive typesโstrings, numbers, booleans, and nullโand two structured typesโobjects and arrays . The format is not perfect: it has no comments, no built-in schema, and no support for binary data. But it is the common language that every language and every platform speaks. The alternatives exist because JSON is not the right choice for every case, and the introduction to the formats is the map of those cases.
Key point: Data serialization converts in-memory structures to storable or transmittable bytes. JSON is the text format that dominates web APIs, with UTF-8 encoding and the RFC 8259 grammar. The alternativesโXML, YAML, and the binary formatsโmake different trade-offs between human readability, schema enforcement, and efficiency.
Why the format choice is the contract
The readability problem. A text format is readable by a human, and the readability is the debugging advantage. A JSON payload can be inspected in the browser’s network tab, a YAML configuration can be edited in a text editor, and an XML document can be validated against its schema. The binary formats are not readable, and debugging requires specialized tools. The readability is the trade-off against size and speed.
The efficiency problem. A text format is larger than the binary format that encodes the same data, and parsing is slower because the text must be tokenized and converted. A binary format like MessagePack encodes the type and length in the bytesโthe spec defines separate formats for nil, booleans, integers of various widths, floats, strings, and arrays, each with a distinct first byte . The parsing is a direct memory read. The efficiency is the trade-off against readability and interoperability.
The schema problem. JSON has no built-in schema, and validation is a separate layer. The JSON Schema is the standard that defines the structure, the types, and the constraints, and the current version is 2020-12, split into Core and Validation vocabularies . XML has XSD, YAML has JSON Schema or custom validators, and Protobuf has the .proto files that define the message types and field numbers . The schema is the trade-off against flexibility: a schema-enforced format is more robust, but it requires the definition and the tooling.
The security problem. JSON parsing is not inherently safe. The JSON.parse() reviver function is called for every key-value pair, and if an attacker can pollute Object.prototype with a parseReviver function through a co-dependency, every JSON response in the process can be silently modified. The Axios CVE-2026-42044 is the concrete example: a polluted parseReviver intercepted every JSON response and could escalate a role: "user" to role: "admin" or inflate a balance: 100 to balance: 999999 without any error . The security is the trade-off against the convenience of the built-in parser.
The ecosystem problem. The format is only as useful as the tools that read and write it. JSON is supported by every language and every platform, and the parsers are built into the standard libraries. YAML has good support in DevOps tooling, XML has enterprise support, and the binary formats have narrower support with the tooling as an extra dependency. The ecosystem is the trade-off against efficiency.
The interoperability problem. The RFC 8259 notes that certain patterns, while allowed by the specification, have caused interoperability problems . The large integers are the classic case: JSON allows arbitrary precision, but JavaScript’s Number is a 64-bit float, so JSON.parse("12345678901234567890") returns 12345678901234568000 because the number is rounded before the reviver is even called . The interoperability is the trade-off against the simplicity of the number model.
a. The JSON grammar and the serialization mechanics
The JSON grammar is defined by RFC 8259. A JSON text is a serialized value, and the value can be an object, an array, a string, a number, a boolean, or null. The object is an unordered collection of name/value pairs, where the name is a string and the value is any of the six types. The array is an ordered sequence of values . The strings must be double-quoted, the property names must be double-quoted, and the single quotes are not valid . The comments are not allowed, the trailing commas are not allowed, and the numbers are the decimal notation.
// The JSON grammar
const value = {
name: "Ada", // string (double-quoted)
age: 36, // number
active: true, // boolean
tags: ["math", "code"], // array
address: null, // null
nested: { city: "London" } // object
};
// The serialization
const json = JSON.stringify(value);
// '{"name":"Ada","age":36,"active":true,"tags":["math","code"],"address":null,"nested":{"city":"London"}}'
// The deserialization
const parsed = JSON.parse(json);
// { name: 'Ada', age: 36, active: true, ... }
The JSON.stringify() method accepts three parameters: the value, an optional replacer that can filter or transform the properties, and an optional space that adds the indentation for readability. The replacer can be a function that receives each key and value, or an array of the property names to include . The toJSON() method on an object is called by JSON.stringify() before the serialization, which is the mechanism that the Date object uses to serialize as an ISO string.
// The replacer and the space
const json = JSON.stringify(value, (key, val) => {
// The function is called for every key-value pair.
// Returning undefined omits the property.
if (key === "password") return undefined;
return val;
}, 2); // The 2-space indentation
// The toJSON method
const date = new Date("2026-01-01");
JSON.stringify(date); // '"2026-01-01T00:00:00.000Z"'
The JSON.parse() method accepts the text and an optional reviver function. The reviver is called for every key-value pair, bottom-up, and the return value replaces the value in the result . The reviver is the mechanism that the Axios vulnerability exploited: the polluted Object.prototype.parseReviver was inherited by the config object, and the JSON.parse(data, this.parseReviver) call invoked it for every response .
// The reviver
const parsed = JSON.parse('{"a":1,"b":2}', (key, val) => {
if (key === "b") return val * 10;
return val;
});
// { a: 1, b: 20 }
b. The variants and the binary formats
The JSON variants extend the grammar for specific use cases. JSON5 allows comments, single quotes, unquoted names, hexadecimal numbers, and trailing commas. It is the “JSON for Humans” . JSONC is the JSON with comments and trailing commas, and it is the format that VS Code accepts. JSON Lines (NDJSON) is the format where each line is a separate JSON document, and it is the format for streaming and line-oriented processing. The NDJSON specification requires each line to be a valid JSON value, and the FHIR specification uses it for bulk data transfer with the MIME type application/fhir+ndjson .
The binary formats are the efficiency alternatives. MessagePack encodes the same data model as JSON, with a compact binary representation: the nil is 0xc0, the booleans are 0xc2 and 0xc3, the integers have separate formats for 8, 16, 32, and 64 bits, and the strings and arrays have length prefixes . The IANA registered application/vnd.msgpack in 2024 . Protobuf is the schema-driven binary format, and the .proto files define the message types, the field numbers, and the wire format. The field numbers are limited to 29 bits because three bits are used for the wire type, and the reusing a field number is a serious mistake . BSON is the binary format that MongoDB uses, and the specification defines the document as an int32 length followed by the elements and a null terminator .
// The JSON Lines format
// Each line is a separate JSON document.
const ndjson = '{"name":"Ada"}\n{"name":"Grace"}\n{"name":"Linus"}\n';
// The parsing
const records = ndjson.trim().split('\n').map(line => JSON.parse(line));
// [{ name: 'Ada' }, { name: 'Grace' }, { name: 'Linus' }]
The JSON Schema is the validation layer. The 2020-12 specification defines the Core and the Validation vocabularies, and the meta-schema that validates the schemas themselves . The $schema keyword declares the version, the $id declares the base URI, the type declares the type, the properties declares the object properties, and the required declares the required properties. The $ref is the mechanism that references another schema, and the unevaluatedProperties is the mechanism that guards against the misspelled properties .
// The JSON Schema
const schema = {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
name: { type: "string" },
age: { type: "integer", minimum: 0 },
},
required: ["name"],
unevaluatedProperties: false,
};
Complete Example Session
// ============================================
// PART 1: THE JSON GRAMMAR
// ============================================
// The six value types: object, array, string, number, boolean, null.
const value = {
name: "Ada",
age: 36,
active: true,
tags: ["math", "code"],
address: null,
};
// The strings and the property names must be double-quoted.
// The single quotes and the comments are not valid.
// ============================================
// PART 2: THE SERIALIZATION
// ============================================
const json = JSON.stringify(value);
// '{"name":"Ada","age":36,"active":true,"tags":["math","code"],"address":null}'
// The replacer filters the properties.
const filtered = JSON.stringify(value, (key, val) => {
if (key === "address") return undefined;
return val;
});
// The space adds the indentation.
const pretty = JSON.stringify(value, null, 2);
// ============================================
// PART 3: THE DESERIALIZATION
// ============================================
const parsed = JSON.parse(json);
// The reviver transforms the values.
const transformed = JSON.parse('{"a":1,"b":2}', (key, val) => {
if (key === "b") return val * 10;
return val;
});
// ============================================
// PART 4: THE LARGE INTEGER PROBLEM
// ============================================
// JSON allows arbitrary precision, but JavaScript does not.
const big = JSON.parse("12345678901234567890");
// 12345678901234568000 (rounded)
// The fix: serialize the number as a string.
const safe = JSON.stringify({ big: "12345678901234567890" });
// ============================================
// PART 5: THE PROTOYPE POLLUTION RISK
// ============================================
// The polluted parseReviver is called for every JSON response.
Object.prototype.parseReviver = (key, val) => {
if (key === "role") return "admin";
return val;
};
const response = JSON.parse('{"role":"user","balance":100}');
// { role: 'admin', balance: 100 } (the role was escalated)
delete Object.prototype.parseReviver;
// ============================================
// PART 6: THE JSON LINES
// ============================================
const ndjson = '{"name":"Ada"}\n{"name":"Grace"}\n';
const records = ndjson.trim().split('\n').map(line => JSON.parse(line));
// ============================================
// PART 7: THE JSON SCHEMA
// ============================================
const schema = {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: { name: { type: "string" } },
required: ["name"],
};
// ============================================
// PART 8: THE MESSAGEPACK
// ============================================
// The nil: 0xc0
// The true: 0xc3
// The false: 0xc2
// The int: 0xcc (uint8), 0xcd (uint16), etc.
// ============================================
// PART 9: THE PROTOBUF
// ============================================
// The message definition:
// message SearchRequest {
// string query = 1;
// int32 page_number = 2;
// }
// The field numbers are the wire identifiers.
// ============================================
// PART 10: THE CHECKLIST
// ============================================
// [ ] The JSON is the default for the APIs.
// [ ] The JSON5 or JSONC is used for the config files with comments.
// [ ] The JSON Lines is used for the streaming.
// [ ] The large integers are serialized as strings.
// [ ] The reviver is not polluted.
// [ ] The JSON Schema is used for the validation.
// [ ] The binary format is used for the high-performance cases.
The ten parts covered the grammar, the serialization, the deserialization, the large integer problem, the prototype pollution risk, the JSON Lines, the JSON Schema, the MessagePack, the Protobuf, and the checklist. The through-line is that the format is the contract, and the choice is the trade-off.
Quick Reference
The text formats
| Format | Readability | Schema | Comments | Use case |
|---|---|---|---|---|
| JSON | High | External (JSON Schema) | No | Web APIs, data storage |
| XML | Medium | XSD | Yes | Enterprise, document-centric |
| YAML | High | External (JSON Schema) | Yes | Configuration, DevOps |
| JSON5 | High | External | Yes | Human-edited config |
| JSONC | High | External | Yes | VS Code config |
| JSON Lines | High | External | No | Streaming, line-oriented |
The binary formats
| Format | Schema | Size | Use case |
|---|---|---|---|
| MessagePack | No | Small | Binary JSON |
| Protobuf | Yes (.proto) | Small | gRPC, high-performance |
| BSON | No | Medium | MongoDB |
The JSON Schema
| Version | Status |
|---|---|
| 2020-12 | Current |
| 2019-09 | Previous |
The serialization edge cases
| Case | Behavior |
|---|---|
undefined | Omitted from the output |
NaN / Infinity | Converted to null |
BigInt | Throws a TypeError |
| Circular reference | Throws a TypeError |
Date | Serialized via toJSON() |
| Large integer | Rounded to the nearest float |
Best Practices
โ Do This:
// Use JSON for the web APIs
const payload = JSON.stringify(data);
// Use JSON5 or JSONC for the config files with comments
// Use YAML for the human-edited config
// Use JSON Lines for the streaming
// Serialize the large integers as strings
const safe = JSON.stringify({ big: "12345678901234567890" });
// Validate the JSON Schema
const valid = validate(schema, data);
โ Don’t Do This:
// Use eval() to parse JSON
const data = eval('(' + json + ')'); // the security risk
// Trust the reviver from the prototype chain
JSON.parse(data, this.parseReviver); // the Axios CVE
// Assume the large integers are exact
JSON.parse("12345678901234567890"); // rounded
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| The JSON parse fails | The trailing comma or the single quote | Use a strict JSON parser |
| The large integer is wrong | JavaScript’s float precision | Serialize as a string |
| The prototype is polluted | The reviver is inherited from the prototype chain | Validate the reviver is an own property |
| The schema is not validated | The JSON Schema is not applied | Add the schema validation |
| The binary format is not readable | The format is not text | Use the tooling to inspect |
Real-World Examples
1. The basic serialization
JSON.stringify({ name: "Ada", age: 36 });
// '{"name":"Ada","age":36}'
2. The pretty-print
JSON.stringify({ name: "Ada" }, null, 2);
// '{\n "name": "Ada"\n}'
3. The replacer
JSON.stringify({ name: "Ada", password: "x" }, (k, v) => k === "password" ? undefined : v);
// '{"name":"Ada"}'
4. The reviver
JSON.parse('{"a":1}', (k, v) => k === "a" ? v * 2 : v);
// { a: 2 }
5. The large integer as a string
JSON.stringify({ big: "12345678901234567890" });
// '{"big":"12345678901234567890"}'
6. The JSON Lines
'{"a":1}\n{"b":2}\n'.trim().split('\n').map(JSON.parse);
// [{ a: 1 }, { b: 2 }]
7. The JSON Schema
{ $schema: "https://json-schema.org/draft/2020-12/schema", type: "object" };
8. The MessagePack nil
// The nil is the single byte 0xc0
9. The Protobuf field number
// message SearchRequest { string query = 1; }
10. The BSON document
// The document is the int32 length + the elements + the null byte
Visual
The Serialization Contract
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE FORMAT IS THE CONTRACT โ
โ โ
โ In-memory structure โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ { name: "Ada", โ โ
โ โ age: 36 } โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โ serialize โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ '{"name":"Ada", โ The bytes โ
โ โ "age":36}' โ โ
โ โโโโโโโโโโโโฌโโโโโโโโโโโ โ
โ โ โ
โ โ deserialize โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ { name: "Ada", โ The structure โ
โ โ age: 36 } โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The JSON Types
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ THE SIX VALUE TYPES โ
โ โ
โ Primitive: โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ string โ โ number โ โ boolean โ โ
โ โ "text" โ โ 42, 3.14 โ โ true/false โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ
โ โโโโโโโโโโโโโโโ โ
โ โ null โ โ
โ โ null โ โ
โ โโโโโโโโโโโโโโโ โ
โ โ
โ Structured: โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โ โ object โ โ array โ โ
โ โ {"key":val} โ โ [val, val] โ โ
โ โโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Binary Format Comparison
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TEXT VS BINARY โ
โ โ
โ JSON (text) MessagePack (binary) โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ {"a":1} โ โ 0x81 0xa1 0x61 0x01 โ โ
โ โ โ โ โ โ
โ โ 7 bytes โ โ 4 bytes โ โ
โ โ Readable โ โ Not readable โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ The binary format is smaller and faster to parse. โ
โ The text format is readable and universally supported. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| JSON | The text format, RFC 8259, UTF-8 |
| XML | The markup format, schema support |
| YAML | The indentation format, comments, JSON superset |
| JSON5 | The JSON for humans, comments, single quotes |
| JSONC | The JSON with comments, VS Code format |
| JSON Lines | The line-oriented JSON, streaming |
| MessagePack | The binary JSON, compact encoding |
| Protobuf | The schema-driven binary, field numbers |
| BSON | The MongoDB binary, length-prefixed |
| JSON Schema | The validation, 2020-12 |
Key takeaways:
- Serialization is the contract between the writer and the reader. The format is the trade-off between human readability, machine efficiency, schema enforcement, and ecosystem support. The choice depends on what the data is for, not on what is fashionable.
- JSON is the default for the web APIs. The RFC 8259 defines the six value typesโfour primitives and two structuredโand the UTF-8 encoding . The format has no comments and no built-in schema, and the JSON Schema is the separate validation layer .
- The JSON variants extend the grammar for specific cases. JSON5 and JSONC add the comments for the config files . JSON Lines (NDJSON) is the line-oriented format for streaming, and it is used by FHIR for bulk data transfer .
- The binary formats are the efficiency alternative. MessagePack encodes the same data model with a compact byte format, and the IANA registered the media type in 2024 . Protobuf is the schema-driven format with the field numbers, and BSON is the MongoDB format with the length-prefixed documents .
- The security and the interoperability are the real risks. The prototype pollution of the reviver function can silently modify every JSON response, and the Axios CVE-2026-42044 is the concrete example . The large integers are rounded by JavaScript’s float precision before the reviver is even called .
Remember: Data serialization is the conversion of the in-memory structures to the bytes that can be stored or transmitted. JSON is the text format that the web speaks, and the RFC 8259 is the grammar. The alternatives exist because JSON is not the right choice for every case: XML for the schema-heavy enterprise, YAML for the human-edited configuration, the binary formats for the efficiency. The format is the contract, and the choice is the trade-off. The security and the interoperability are the costs that the choice must account for.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!