JSONifyPro v2.5 DEVELOPER HUB
Menu

Track 3 / Guide 18

MongoDB BSON vs Standard JSON

Understand BSON types, Extended JSON, document-size tradeoffs, and MongoDB storage compression without confusing binary encoding with smaller data.

JSONifyPro Engineering9 min read
On this page

BSON is a typed binary representation

BSON represents documents using binary type information and length-prefixed structures. It is not simply compressed JSON text. Its type model includes values that ordinary JSON does not express directly, such as object identifiers, dates, binary data, and specific numeric representations. Converting between the two requires a policy for preserving those distinctions.

Do not assume binary means smaller. Type markers, field names, lengths, and terminators contribute overhead, and the result depends on the document shape. A tiny document can be larger in BSON than in compact JSON, while another workload may benefit from binary numeric values or other representation choices. Measure equivalent logical data rather than comparing a typed document with a lossy text projection.

Separate BSON encoding from the database storage engine. On-disk compression, indexes, and allocation behavior affect storage size independently of the standalone serialized document. The PostgreSQL JSONB guide describes another internal document representation; despite similar names and goals, JSONB and BSON are not interchangeable wire formats.

Preserve identity, dates, decimals, and binary values

An ObjectId has a defined binary representation and common textual form, but replacing it with an arbitrary string changes its type. Applications should decide whether public APIs expose that textual form as an opaque identifier or preserve the BSON type through an extended representation. Do not assume that every string resembling an identifier should be automatically converted.

A BSON date represents a timestamp value with a specific unit, not a formatted local calendar string. Converting it to JSON requires a documented form such as an extended representation or a timezone-explicit string. Keep dates distinct from other timestamp-like BSON types used for different purposes. Similar printed values do not imply interchangeable semantics.

Decimal128 can preserve decimal values that a JavaScript number cannot represent exactly. Converting it through Number can lose the benefit before export. Binary values also need an encoding and subtype policy when represented as text, often adding size overhead. Define these mappings before building ETL jobs or public responses. A successful JSON parse at the destination does not prove that the original BSON types survived.

Canonical and relaxed Extended JSON serve different needs

Extended JSON uses structured representations to carry BSON type information in JSON syntax. Canonical mode emphasizes type preservation, while relaxed mode emphasizes readability where possible. Choose according to the workflow: an archival or migration path may need stronger type fidelity than a human-facing API response.

Do not confuse canonical Extended JSON with a cryptographic canonicalization scheme for arbitrary JSON. Its purpose is representation of BSON types, not a universal promise that independently formatted documents produce identical bytes for signatures. If exact-byte signing is needed, define that protocol separately and preserve the required representation.

Use the driver's supported conversion APIs instead of hand-assembling reserved-key objects. Manual conversion is easy to get wrong for numeric boundaries, dates, and binary subtypes. Test round trips by examining types and values, not just the printed output. A relaxed conversion can look correct while changing an integer or decimal representation in ways that matter to downstream arithmetic or schema checks.

A runnable BSON and Extended JSON round trip

The program below builds a document containing several BSON-specific values, serializes it, and compares canonical Extended JSON size with BSON byte size. It then parses the extended representation and checks important types. Install bson 6.x and run the file with Node.js. No database connection is needed, so the experiment isolates encoding and conversion behavior.

The byte counts are measurements of this synthetic document only. They do not establish a universal size ranking or database compression ratio. The example includes a decimal value and a large integer represented with driver types so it does not route them through an imprecise JavaScript number. Keep that discipline when adapting the pattern to imported data.

The assertions inspect values after conversion, which is more useful than assuming a visually similar printout is equivalent. Add binary subtype checks and date-range fixtures for your own contract. If an API intentionally exports simpler strings instead of Extended JSON, test that mapping separately and document that the public representation is no longer a native BSON type.

Node.js 20+ · npm install bson@6 · node bson-demo.cjs
const { BSON, EJSON, ObjectId, Decimal128, Long, Binary } = require("bson");

const document = {
  _id: new ObjectId("64b000000000000000000001"),
  createdAt: new Date("2026-01-01T00:00:00.000Z"),
  amount: Decimal128.fromString("19.95"),
  sequence: Long.fromString("9007199254740993"),
  attachment: new Binary(Buffer.from([1, 2, 3]))
};

const bytes = BSON.serialize(document);
const text = EJSON.stringify(document, { relaxed: false });
const restored = EJSON.parse(text, { relaxed: false });

if (!(restored._id instanceof ObjectId)) throw new Error("ObjectId lost");
if (restored.amount.toString() !== "19.95") throw new Error("Decimal lost");
if (restored.sequence.toString() !== "9007199254740993") {
  throw new Error("Integer precision lost");
}
console.log({
  bsonBytes: bytes.length,
  canonicalJsonBytes: Buffer.byteLength(text),
  createdAt: restored.createdAt.toISOString()
});

Document shape influences size and access cost

Field names remain part of a BSON document, so repeated long names are not free. Arrays also have representation overhead. Renaming every field to a single letter might reduce some bytes but can damage maintainability and external contracts. Evaluate compression and access patterns before sacrificing meaningful names for a speculative saving.

Embedding related data can make a read efficient, but unbounded arrays cause growth and update problems. Keep collections with independent lifecycles or potentially large cardinality under deliberate modeling. The document limit is a hard operational constraint, not a target size for ordinary records. Leave room for growth and reject uncontrolled expansion through application and database validation.

Compare the complete query path, including indexes and returned fields. A compact document still performs poorly if every request scans a collection or returns large unused values. Projection and appropriate indexing can matter more than small encoding differences. The serialization benchmark guide explains why representation-level size and end-to-end latency should be measured as separate dimensions.

Storage compression is not per-document wire size

WiredTiger storage compression operates within the storage engine, so collection storage statistics reflect more than summing independently serialized BSON sizes. Pages, indexes, allocation, and compression behavior all contribute. Do not compare a driver's byte count directly with disk usage and assume the difference is a simple compression ratio.

Measure logical data size, storage size, index size, and workload behavior using the database's supported statistics. Record compression settings and version. Repeated field names and similar values can compress well across stored data, while already compressed binary content may offer little benefit. Results depend on the dataset rather than the word binary in the format name.

Network compression is another distinct layer. A database driver communicating with a server may use negotiated compression, while a public JSON API has its own transport policy. Compare the actual deployed paths when estimating bandwidth. Avoid a benchmark that compresses one representation but not another without labeling the difference. Storage efficiency, transfer efficiency, and parsing CPU can move in different directions.

ETL pipelines need an explicit type map

When exporting to JSON, decide how each extended type is represented and how the importer reconstructs it. A date string should have a timezone convention; a decimal should retain exact spelling or a defined scale; a large integer should avoid float conversion; binary data should preserve its subtype when that matters. Put the map in tests rather than relying on a tool's default output forever.

For record-oriented exports, NDJSON can make processing and restart easier, but it does not resolve type mapping. Each line can contain canonical Extended JSON or a domain-specific projection. The NDJSON guide explains framing and checkpoint semantics that complement the type contract.

Validate imported records before writing them into the target collection. Distinguish missing fields from nulls and check historical variants intentionally. A migration that silently coerces all unfamiliar values into strings may finish without errors while changing query behavior. Keep source and destination versions, conversion settings, and a small fidelity corpus with the migration artifact so a later replay can reproduce the same semantics.

Test compatibility and operational limits together

Build fixtures for ObjectId, dates, Decimal128, integer boundaries, binary subtypes, nested arrays, missing values, and null. Compare native types after a round trip through BSON and Extended JSON. Add a domain-specific JSON export test if that is what clients actually consume. The public API may intentionally choose a different representation, but the choice should be explicit.

Test document growth and query performance with realistic distributions. A single small fixture cannot reveal unbounded embedded arrays, index overhead, or compression behavior. Measure writes as well as reads, because additional indexes and large document updates consume resources. Keep benchmarks in an isolated database and retain the scripts and configuration.

Finally, separate the benefits you actually need. BSON offers a richer typed representation for the MongoDB ecosystem. JSON offers broad interoperability and straightforward inspection. Neither is universally smaller or faster, and neither removes the need for schema discipline. A dependable system documents its type map, bounds document growth, measures the deployed storage and transport paths, and verifies that conversions preserve the values the business relies on.

Compare exports by fidelity level

Prepare three explicit representations when evaluating a migration: native BSON, canonical Extended JSON, and the domain-specific JSON sent to external clients. They may intentionally preserve different information. A public identifier string can be appropriate even when an archival export must preserve the native type. Label the purpose of each representation before comparing size.

For every conversion, inspect types after reconstruction. A date that prints the same text may have become a string, and a large integer may have become an imprecise number. Test arithmetic or query behavior where type matters, not just visual equality. Include values near numeric boundaries and dates outside the most common range.

Binary data requires a subtype policy as well as byte preservation. A text export that keeps the bytes but loses subtype information may not be equivalent for the consuming application. Use driver-supported Extended JSON conversion when fidelity is required and document any deliberate simplification in public APIs.

Do not automatically interpret every object containing reserved-looking keys as a trusted extended value in an unrelated API. The endpoint's contract should state whether it accepts Extended JSON or ordinary domain JSON. Keeping those boundaries distinct avoids surprising coercions in general user data.

Measure collection growth under realistic document evolution

A new collection of small documents can look efficient while later updates produce large embedded histories or arrays. Model expected growth over the record lifecycle. If data grows without a natural bound, consider separate records with explicit relationships rather than appending indefinitely inside one document.

Measure logical size, storage size, and index size after representative writes and updates. Compression ratios depend on repeated structure, value entropy, and storage behavior. Already compressed attachments may dominate a document without benefiting much from another compression layer. Consider whether large binary objects belong in the document store at all under the application's access pattern.

Test reads with projection so the benchmark matches what clients need. Fetching an entire document to return two fields adds network and decoding work that a smaller representation cannot fully solve. Index and projection design should be evaluated together with the document model.

For migrations, keep counts and checksums or domain-level totals where appropriate, plus a sample type-fidelity corpus. Successful command completion is not enough evidence that decimals, dates, and identifiers survived. Validate the destination through the same queries the application will use.

Finally, retain conversion settings and driver versions with the migration record. Defaults can change over time, and a rerun should not silently produce a different type map. A dependable BSON-to-JSON workflow is reproducible at both the byte-processing and application-semantics levels.

Keep a fixture for an empty binary value, a decimal with trailing scale, and a large signed integer. These cases expose conversion assumptions that ordinary sample documents miss. Inspect both reconstructed types and application-visible values, and document whether scale preservation is required by the business contract rather than merely by display formatting.

For database migrations, verify application queries after import rather than stopping at a document count. A type change can preserve the number of records while altering sort order, equality, or index use. Include representative query results in the acceptance criteria so storage fidelity is connected to actual application behavior.

Engineering Comparison

BSON values and JSON export decisions
BSON valuePlain JSON challengePreserving approachRisk to test
ObjectIdNo native identifier typeExtended JSON or documented stringAccidental type coercion
DateNo native date typeExtended JSON or timezone stringUnit and timezone loss
Decimal128Binary float is not exact decimalCanonical extended formRounding through Number
Int64May exceed safe JS integerLong-aware extended formPrecision loss
BinaryNo byte-array scalarExtended form with subtypeSubtype or encoding loss
DocumentStorage and type semantics differExplicit field mappingUnknown historical variants

BSON is not compressed JSON. The example measures one document's encoding size; collection storage and network compression require separate database and transport measurements.

Primary References