Skip to content

DOCSP-47011: Extended JSON #528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 18 additions & 3 deletions source/data-formats.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
.. This page is hidden from the TOC and search indexing.

.. meta::
:robots: noindex, nosnippet

.. _golang-data-formats:

========================
Expand All @@ -15,12 +20,22 @@ Specialized Data Formats
:values: reference

.. meta::
:keywords: bson, uuid, date, time

.. TODO Landing page
:keywords: bson, JSON, date, time, marshal, unmarshal, serialization

.. toctree::
:titlesonly:
:maxdepth: 1

BSON </data-formats/bson>
Extended JSON </data-formats/extended-json>

Overview
--------

You can use several types of specialized data formats in the
{+driver-short+}. To learn how to work with these data formats, see the
following guides:

- Learn how to work with BSON documents in the :ref:`golang-bson` guide.
- Learn how to convert between {+language+} types and Extended JSON in the
:ref:`golang-extended-json` guide.
260 changes: 260 additions & 0 deletions source/data-formats/extended-json.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
.. _golang-extended-json:

===================================
Document Data Format: Extended JSON
===================================

.. contents:: On this page
:local:
:backlinks: none
:depth: 2
:class: singlecol

.. facet::
:name: genre
:values: reference

.. meta::
:keywords: code example, bson, relaxed, canonical, legacy

Overview
--------

JSON is a data format that represents the values of objects, arrays, numbers,
strings, booleans, and nulls. The **Extended JSON** format defines a reserved
set of keys prefixed with the ``$`` character to represent field type
information that directly corresponds to each type in BSON, the format
that MongoDB uses to store data.

Extended JSON Formats
---------------------

MongoDB Extended JSON features different string formats to represent
BSON data. Each of the formats conforms to the `JSON RFC
<https://datatracker.ietf.org/doc/html/rfc8259>`__ and meets specific
use cases.

The **Extended** format, also known as the **Canonical**
format, features specific representations for every BSON type for
bidirectional conversion without loss of information. The **Relaxed**
format is more concise and closer to ordinary JSON, but does not
represent all the type information such as the specific byte size of
number fields.

The following table provides descriptions of the JSON formats:

.. list-table::
:header-rows: 1
:stub-columns: 1
:widths: 10 40

* - Name
- Description

* - **Extended**
- | Also known as the *canonical* format, this JSON representation avoids loss of
BSON type information.
| This format prioritizes type preservation at the loss of human-readability and
interoperability with older formats.

* - **Relaxed Mode**
- | JSON representation that describes BSON documents with some type information loss.
| This format prioritizes human-readability and interoperability at the loss of
certain type information.

* - **Shell**
- | JSON representation that matches the syntax used in the MongoDB shell.
| This format prioritizes compatibility with the MongoDB shell, which often uses
JavaScript functions to represent types.

* - **Strict**
- | *Deprecated* This representation is the legacy format that fully conforms to
the JSON RFC and allows any JSON parser to read the type information.

To learn more about JSON, BSON, and Extended JSON, see our
:website:`article about JSON and BSON </resources/basics/json-and-bson>`
and the :manual:`Extended JSON </reference/mongodb-extended-json/>`
reference in the {+mdb-server+} manual.

.. _golang-extended_json_examples:

Extended JSON Examples
~~~~~~~~~~~~~~~~~~~~~~

The following tabs show a document containing an ObjectId, date, and long
number field represented in each Extended JSON format. Select from the
tabs to see the same data presented in each JSON format:

.. tabs::

.. tab:: Extended
:tabid: extended-format

.. code-block:: json

{
"_id": { "$oid": "573a1391f29313caabcd9637" },
"createdAt": { "$date": { "$numberLong": "1601499609" }},
"numViews": { "$numberLong": "36520312" }
}

.. tab:: Relaxed Mode
:tabid: relaxed-mode-format

.. code-block:: json

{
"_id": { "$oid": "573a1391f29313caabcd9637" },
"createdAt": { "$date": "2020-09-30T18:22:51.648Z" },
"numViews": 36520312
}

.. tab:: Shell
:tabid: shell-format

.. code-block:: json

{
"_id": ObjectId("573a1391f29313caabcd9637"),
"createdAt": ISODate("2020-09-30T18:22:51.648Z"),
"numViews": NumberLong("36520312")
}

.. tab:: Strict
:tabid: strict-format

.. code-block:: json

{
"_id": { "$oid": "573a1391f29313caabcd9637" },
"createdAt": { "$date": 1601499609 },
"numViews": { "$numberLong": "36520312" }
}

Read Extended JSON
------------------

You can read an Extended JSON string into a {+language+} struct by
calling the ``bson.UnmarshalExtJSON()`` method. This method parses an
Extended JSON string and stores the result in the specified value parameter.

This example shows how you can read an Extended JSON string
into the following ``Person`` struct:

.. literalinclude:: /includes/data-formats/ejson.go
:start-after: begin-person-struct
:end-before: end-person-struct
:language: go
:dedent:

The following code uses the ``UnmarshalExtJSON()`` method to read an
Extended JSON string and unmarshal the data into an instance of ``Person``:

.. io-code-block::
:copyable: true

.. input:: /includes/data-formats/ejson.go
:start-after: begin-unmarshal
:end-before: end-unmarshal
:language: go
:emphasize-lines: 4
:dedent:

.. output::
:language: none
:visible: false

Go Struct Representation:
{ID:ObjectID("578f6fa2df35c7fbdbaed8c5") Name:Liana Ruiz Age:46 Birthday:569203200000 Address:{Street:500 Slippery Rock Road City:Round Rock State:AR} Hobbies:[cycling baking]}

Write Extended JSON
-------------------

You can produce an Extended JSON string from an instance of a
{+language+} struct by calling the ``bson.MarshalExtJSON()`` method.
The following example creates an Extended JSON string in the Relaxed
format from an instance of ``Person``:

.. io-code-block::
:copyable: true

.. input:: /includes/data-formats/ejson.go
:start-after: begin-marshal
:end-before: end-marshal
:language: go
:emphasize-lines: 10
:dedent:

.. output::
:language: none
:visible: false

Extended JSON Representation:
{"_id":{"$oid":"686688fa7c1a2e75405f4697"},"name":"Matteo Carisi","age":49,"birthday":{"$date":"1975-10-30T00:00:00Z"},"address":{"street":"14a Corner Court","city":"Springfield","state":"IL"},"hobbies":["cooking","birdwatching"]}

The second parameter to ``MarshalExtJSON()`` determines if the output
string is in Canonical (Extended) format or Relaxed format. The
preceding example passes ``false`` as the ``canonical`` parameter, so
the output is Relaxed JSON.

.. note:: Dates Before Epoch Time

When you marshal a date value that is before January 1, 1970,
00:00:00 UTC (Epoch time), it appears as a Unix timestamp in Relaxed
JSON. If the date is after the Epoch time, it appears in a readable
date format.

Format Extended JSON
~~~~~~~~~~~~~~~~~~~~

You can use the ``bson.MarshalExtJSONIndent()`` method to print a
formatted Extended JSON string that includes newlines, prefixes, and
indentation.

The following code uses the ``MarshalExtJSONIndent()`` method to print
the JSON string from the preceding example formatted with two spaces of
indentation:

.. io-code-block::
:copyable: true

.. input:: /includes/data-formats/ejson.go
:start-after: begin-marshal-fmt
:end-before: end-marshal-fmt
:language: go
:emphasize-lines: 10
:dedent:

.. output::
:language: none
:visible: false

{
"_id": {
"$oid": "686688fa7c1a2e75405f4697"
},
"name": "Matteo Carisi",
"age": 49,
"birthday": {
"$date": "1975-10-30T00:00:00Z"
},
"address": {
"street": "14a Corner Court",
"city": "Springfield",
"state": "IL"
},
"hobbies": [
"cooking",
"birdwatching"
]
}

API Documentation
-----------------

To learn more about the methods and types used in this guide, see the
following API documentation:

- `UnmarshalExtJSON() <{+api+}/bson#UnmarshalExtJSON>`__
- `MarshalExtJSON() <{+api+}/bson#MarshalExtJSON>`__
- `MarshalExtJSONIndent() <{+api+}/bson#MarshalExtJSONIndent>`__
70 changes: 70 additions & 0 deletions source/includes/data-formats/ejson.go
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Use a consistent pattern for the print in L40, L58, and L67.

Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"fmt"
"log"
"time"

"go.mongodb.org/mongo-driver/v2/bson"
)

// begin-person-struct
type Person struct {
ID bson.ObjectID `bson:"_id"`
Name string
Age int
Birthday bson.DateTime
Address Address
Hobbies []string
}

type Address struct {
Street string
City string
State string
}

// end-person-struct

func main() {

// begin-unmarshal
var extJsonString = "{\"_id\":{\"$oid\":\"578f6fa2df35c7fbdbaed8c5\"},\"name\":\"Liana Ruiz\",\"age\":46,\"birthday\":{\"$date\":\"1988-01-15T00:00:00Z\"},\"address\":{\"street\":\"500 Slippery Rock Road\",\"city\":\"Round Rock\",\"state\":\"AR\"},\"hobbies\":[\"cycling\", \"baking\"]}"

var p Person
err := bson.UnmarshalExtJSON([]byte(extJsonString), false, &p)
if err != nil {
log.Fatal(err)
}

fmt.Printf("Go Struct Representation:\n%+v\n", p)
// end-unmarshal

// begin-marshal
var person = Person{
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

person is redeclared from L34.

ID: bson.NewObjectID(),
Name: "Matteo Carisi",
Age: 49,
Birthday: bson.NewDateTimeFromTime(time.Date(1975, 10, 30, 0, 0, 0, 0, time.UTC)),
Address: Address{Street: "14a Corner Court", City: "Springfield", State: "IL"},
Hobbies: []string{"cooking", "birdwatching"},
}

newExtJsonString, err := bson.MarshalExtJSON(person, false, true)
if err != nil {
log.Fatal(err)
}

fmt.Printf("Extended JSON Representation:\n%s\n", newExtJsonString)
// end-marshal

// begin-marshal-fmt
formattedString, err := bson.MarshalExtJSONIndent(person, false, true, "", " ")
if err != nil {
log.Fatal(err)
}

fmt.Printf("%s\n", formattedString)
// end-marshal-fmt

}
Loading