BSON Decoder & Extended JSON Viewer
BSON is the binary form MongoDB stores and speaks: a little-endian int32 byte count, a run of elements — each one a type code, a NUL-terminated field name and a value whose length the type decides — and a single zero byte to close. This decoder reads those bytes directly in your browser and shows the document three ways at once: a field tree that names each element's type and its type byte and says what the element costs in bytes, canonical Extended JSON, and relaxed Extended JSON. All twenty-one element types the specification defines are handled, including the deprecated ones you only meet in old data — undefined (0x06), DBPointer (0x0C), symbol (0x0E), and binary subtype 0x02, which carries a second length inside its own payload and hands four junk bytes to every reader that forgets it. Numbers are where a browser-based decoder usually goes wrong. int64, the UTC datetime and MongoDB's internal timestamp are all 64-bit, and a JavaScript number holds only 53 bits of integer, so 9223372036854775807 quietly becomes 9223372036854775808 the moment it passes through one; here every one of them is read and printed from a BigInt instead. decimal128 is decoded from its own 128-bit coefficient and biased exponent straight to an exact decimal string, never approximated through a double — which is the entire reason the type exists for money. The tree also opens an ObjectId into the three parts it actually has, a four-byte creation time, five random bytes and a three-byte counter, and shows a UTC datetime both as its raw signed millisecond count and as a date, so the negative numbers that pre-1970 dates produce stop being a surprise. What it will not do is guess. A document whose declared length disagrees with the file, one that never reaches its terminator, one carrying a type byte the spec does not define, or a JSON file offered by mistake, is refused by name and byte offset rather than half-parsed into a plausible-looking tree.
How to use
- Drop a .bson file on the box — output from mongodump, a single document saved by a driver, or anything else a MongoDB tool wrote.
- Read the tree. Each row is one element with its BSON type name, the type byte as it appears on the wire, its value, and the bytes it costs, so an oversized document explains itself.
- Take canonical Extended JSON when the types have to survive — $numberInt and $numberLong are different things — and relaxed Extended JSON when you only want to read the data.
- Check the extra line under an ObjectId for its creation time, random bytes and counter, and the one under a UTC datetime for the date behind the raw millisecond count.
- If the file is refused, read which error it is: a length that disagrees with the file, a missing terminator and an undefined type byte are three different faults, and the byte offset says where to look.
Frequently asked questions
- Why is a BSON array so much bigger than the same JSON array?
- Because BSON has no array type of its own. An array is stored as an ordinary document whose field names are the decimal indices "0", "1", "2" and so on, each one a real NUL-terminated string on the wire. A thousand 32-bit integers therefore cost a type byte, an index name of one to three characters, a terminator and four bytes of payload each — roughly 8.9 KB where the values alone are 4 KB. It is also why the index keys can be wrong without anything noticing: drivers read arrays by position, so an array keyed "0", "2", "x" decodes into three elements and only misbehaves when something re-serialises it. This tool flags that case rather than silently smoothing it over.
- What is the difference between canonical and relaxed Extended JSON?
- Extended JSON is the standard way to write BSON as text, and it comes in two flavours because two different jobs need it. Canonical wraps every value in a type marker — {"$numberInt": "1"} is not {"$numberLong": "1"} and neither is {"$numberDouble": "1.0"} — so a document can go out to text and come back as exactly the same bytes. Relaxed drops the wrapper wherever plain JSON can hold the value: numbers become numbers, and a date inside the range ISO 8601 covers becomes a readable timestamp. Relaxed is much easier to read and is what mongoexport writes by default, but it is lossy: once a 1 is just a 1, nothing says which of the three numeric types it came from.
- Which BSON values can JavaScript not represent, and what happens to them here?
- Three of them. int64 and the UTC datetime are signed 64-bit integers and the internal timestamp is two unsigned 32-bit halves in one 64-bit word, while a JavaScript number is a double and only holds integers exactly up to 9,007,199,254,740,991. Anything larger is rounded on the way in, which is how 9223372036854775807 turns into 9223372036854775808 in so many viewers. This decoder reads all three with BigInt and prints the digits from the BigInt, so nothing is ever routed through a double. decimal128 is worse still: it is a decimal type precisely so that 0.1 and 2.675 stay exact, and reading it into a double defeats the reason it was chosen. Here it is decoded from its 128-bit coefficient and biased exponent straight to a decimal string.
- Why does a date before 1970 show up as a negative number?
- Because that is exactly what BSON stores. The UTC datetime type is int64 milliseconds since the Unix epoch, and the integer is signed, so any instant before 1970-01-01 is a negative count — a birth date in 1953 is around −526,608,000,000. That is legal and every driver round-trips it correctly, but a surprising number of tools treat the field as unsigned or clamp it at zero, which is how a 1953 date becomes 1970 or jumps to the year 292 million. The range the type can hold is far wider than any calendar most software supports, so this tool shows the raw millisecond count next to the date and says plainly when a value falls outside what a calendar date can express.
- What is binary subtype 0x02, and why is it called a parser trap?
- It is the original, now deprecated, binary layout. Every binary field is written as an int32 length, a subtype byte, then the payload — except subtype 0x02, whose payload begins with a second int32 repeating the length, four less than the first. A reader that treats it like any other subtype hands back a blob with four extra bytes glued to the front, and because those bytes look like plausible binary data nothing complains; the corruption only surfaces when someone tries to use the value. Old collections from the pre-2.0 era still contain these, so a decoder that claims to read BSON has to special-case it. This one does, and refuses the field outright when the inner length does not agree with the outer one.
- What is actually inside an ObjectId? Is it a UUID?
- No — it is 12 bytes with structure, and knowing the structure is usually the point of looking at one. The first four bytes are a big-endian Unix timestamp in seconds, which is why sorting by _id sorts roughly by creation time and why you can filter by date without a separate field. The next five bytes are random per process, and the last three are a counter that starts at a random value and increments per document. Older MongoDB versions used a machine identifier and process id in the middle instead, which leaked a little about the server; that changed in 3.4. There is no checksum and no version field, so any 24 hexadecimal characters are a syntactically valid ObjectId, and only the embedded timestamp tells you whether one is plausible.
Related tools
Hex Dump Viewer
Inspect text or a small file as offset + hex bytes + printable ASCII, like `xxd` or `hexdump -C`.
JSON to Mongoose Schema Generator
Turn a JSON object into a Mongoose schema with type-mapped paths, in your browser.
Java .properties Parser
Parse a .properties file exactly as java.util.Properties.load does — separators, escapes, continuations — and see every line that will surprise you.
ULID Decoder (Timestamp & Randomness)
Decode a ULID into its 48-bit creation timestamp and 80-bit randomness — Crockford Base32, case-insensitive, with ISO UTC and epoch milliseconds.
MessagePack Decoder
Paste base64 or hex MessagePack bytes and read the values — types, nested maps, binary blobs and timestamps — decoded in your browser.
ASCII Code Table
Browse and search the 256-row ASCII / extended-ASCII table — decimal, hex, octal, binary, and the named control characters.