Skip to content

DOCSP-51341: Extended JSON #116

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 16, 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
7 changes: 2 additions & 5 deletions source/data-formats.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ Data Formats
Data Classes </data-formats/data-format-data-class>
BSON </data-formats/bson>
Time Series </data-formats/time-series>

.. TODO: /data-formats/extended-json
Extended JSON </data-formats/extended-json>

Overview
--------
Expand All @@ -49,6 +48,4 @@ sections:
:ref:`kotlin-sync-data-format-data-classes` guide.
- Learn how to work with the BSON data format in the :ref:`kotlin-sync-bson` guide.
- Learn how to store and interact with time series data in the :ref:`kotlin-sync-time-series` guide.

.. TODO: Uncomment these as pages get built
.. - Learn how to use the Extended JSON format in the :ref:`kotlin-sync-extended-json` guide.
- Learn how to read and write Extended JSON strings in the :ref:`kotlin-sync-extended-json` guide.
198 changes: 198 additions & 0 deletions source/data-formats/extended-json.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
.. _kotlin-sync-extended-json:

============================
Work with Extended JSON Data
============================

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

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

.. meta::
:keywords: code examples, bson, relaxed, canonical

.. sharedinclude:: dbx/extended-json.rst

.. replacement:: driver-specific-text-extended

.. replacement:: driver-specific-text-relaxed

The {+driver-short+} uses Relaxed mode by default.

.. replacement:: driver-specific-text-shell

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

This section shows how to read Extended JSON values into {+language+}
objects in the following ways:

- :ref:`kotlin-sync-read-ejson-doc`
- :ref:`kotlin-sync-read-ejson-jsonreader`

.. _kotlin-sync-read-ejson-doc:

Use the Document Classes
~~~~~~~~~~~~~~~~~~~~~~~~

To read a Extended JSON string into a {+language+} document object, call
the ``parse()`` static method from the ``Document`` or ``BsonDocument`` class.
This method parses the Extended JSON string and stores its data in an instance of the specified
document class.

The following example uses the ``parse()`` method to read an Extended JSON string
into a ``Document`` object:

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

.. input:: /includes/data-formats/ext-json.kt
:language: kotlin
:start-after: start-read-doc
:end-before: end-read-doc
:dedent:

.. output::
:visible: false

Document{{_id=507f1f77bcf86cd799439011, myNumber=4794261}}

.. _kotlin-sync-read-ejson-jsonreader:

Use the JsonReader Class
~~~~~~~~~~~~~~~~~~~~~~~~

To read an Extended JSON string into {+language+} objects without using
the {+driver-short+}'s document classes, use the BSON library's ``JsonReader`` class.
This class contains methods to sequentially parse the fields and values
of the Extended JSON string and return them as {+language+} objects.
The driver's document classes also use this class to parse Extended JSON.

The following code uses methods provided by the ``JsonReader`` class to convert
an Extended JSON string into {+language+} objects:

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

.. input:: /includes/data-formats/ext-json.kt
:language: kotlin
:start-after: start-read-bson
:end-before: end-read-bson
:dedent:

.. output::
:visible: false

507f1f77bcf86cd799439011 is type: org.bson.types.ObjectId
4794261 is type: kotlin.Long

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

This section shows how to write Extended JSON values from {+language+}
objects in the following ways:

- :ref:`kotlin-sync-write-ejson-doc`
- :ref:`kotlin-sync-write-ejson-jsonwriter`

.. _kotlin-sync-write-ejson-doc:

Use the Document Classes
~~~~~~~~~~~~~~~~~~~~~~~~

To write an Extended JSON string from a ``Document`` or ``BsonDocument``
object, call the ``toJson()`` method. You can pass this method a
``JsonWriterSettings`` object parameter to specify the Extended JSON format.

The following example writes ``Document`` data as Relaxed mode Extended
JSON:

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

.. input:: /includes/data-formats/ext-json.kt
:language: kotlin
:start-after: start-write-doc
:end-before: end-write-doc
:dedent:

.. output::
:visible: false

{"_id": {"$oid": "507f1f77bcf86cd799439012"}, "createdAt": {"$date": "2020-09-30T21:00:09Z"}, "myNumber": 4794261}

.. _kotlin-sync-write-ejson-jsonwriter:

Use the JsonWriter Class
~~~~~~~~~~~~~~~~~~~~~~~~

To output an Extended JSON string from data stored in {+language+} objects, you can use
the BSON library's ``JsonWriter`` class. To construct a ``JsonWriter`` object,
pass a subclass of a Java ``Writer`` to specify how you want to output the Extended
JSON. Optionally, you can pass a ``JsonWriterSettings`` instance to specify options,
such as the Extended JSON format. By default, ``JsonWriter`` uses the Relaxed mode
format. The {+driver-short+}'s document classes also use this class to convert BSON
to Extended JSON.

The following example uses a ``JsonWriter`` object to create
Canonical mode Extended JSON string values and output them to ``System.out``:

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

.. input:: /includes/data-formats/ext-json.kt
:language: kotlin
:start-after: start-write-bson
:end-before: end-write-bson
:dedent:

.. output::
:visible: false

{"_id": {"$oid": "507f1f77bcf86cd799439012"}, "myNumber": {"$numberLong": "11223344"}}

Custom BSON Type Conversion
---------------------------

In addition to specifying the Extended JSON output format, you
can further customize the output by adding converters to your
``JsonWriterSettings`` object. These converter methods specify logic
for handling different data types during the conversion process.

The following example converts the same document as the :ref:`kotlin-sync-write-ejson-doc`
example. However, this example defines the ``objectIdConverter()`` and ``dateTimeConverter()``
converter methods in a ``JsonWriterSettings`` object to simplify the Relaxed mode
Extended JSON output:

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

.. input:: /includes/data-formats/ext-json.kt
:language: kotlin
:start-after: start-custom-conversion
:end-before: end-custom-conversion
:dedent:

.. output::
:visible: false

{"_id": "507f1f77bcf86cd799439012", "createdAt": "2020-09-30T21:00:09Z", "myNumber": 4794261}

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

To learn more about any of the methods or types discussed in this
guide, see the following API documentation:

- `Document <{+api-root+}/bson/org/bson/Document.html>`__
- `Document.parse() <{+api-root+}/bson/org/bson/Document.html#parse(java.lang.String)>`__
- `JsonReader <{+api-root+}/bson/org/bson/json/JsonReader.html>`__
- `JsonWriter <{+api-root+}/bson/org/bson/json/JsonWriter.html>`__
- `Document.toJson() <{+api-root+}/bson/org/bson/Document.html#toJson()>`__
- `JsonWriterSettings <{+api-root+}/bson/org/bson/json/JsonWriterSettings.html>`__
105 changes: 105 additions & 0 deletions source/includes/data-formats/ext-json.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.example

import org.bson.json.JsonWriter
import org.bson.json.JsonWriterSettings
import java.util.Date
import org.bson.Document
import org.bson.json.JsonMode
import org.bson.json.JsonReader
import org.bson.types.ObjectId
import java.time.Instant
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.io.BufferedWriter
import java.io.OutputStreamWriter

fun main() {

// start-read-doc
val ejsonStr = """
{
"_id": { "$${"oid"}": "507f1f77bcf86cd799439011" },
"myNumber": { "$${"numberLong"}": "4794261" }
}
""".trimIndent()

val doc = Document.parse(ejsonStr)
println(doc)
// end-read-doc

// start-read-bson
val string = """
{
"_id": { "$${"oid"}": "507f1f77bcf86cd799439011" },
"myNumber": { "$${"numberLong"}": "4794261" }
}
""".trimIndent()

val jsonReader = JsonReader(string)
jsonReader.readStartDocument()

// Reads the "_id" field value
jsonReader.readName("_id")
val id = jsonReader.readObjectId()

// Reads the "myNumber" field value
jsonReader.readName("myNumber")
val myNumber = jsonReader.readInt64()

jsonReader.readEndDocument()

println("$id is type: ${id::class.qualifiedName}")
println("$myNumber is type: ${myNumber::class.qualifiedName}")

jsonReader.close()
// end-read-bson

// start-write-doc
val doc = Document()
.append("_id", ObjectId("507f1f77bcf86cd799439012"))
.append("createdAt", Date.from(Instant.ofEpochMilli(1601499609000L)))
.append("myNumber", 4794261)

val settings = JsonWriterSettings.builder()
.outputMode(JsonMode.RELAXED)
.build()

println(doc.toJson(settings))
// end-write-doc

// start-write-bson
val settings = JsonWriterSettings.builder()
.outputMode(JsonMode.EXTENDED)
.build()

JsonWriter(BufferedWriter(OutputStreamWriter(System.out)), settings).use { jsonWriter ->
jsonWriter.writeStartDocument()
jsonWriter.writeName("_id")
jsonWriter.writeObjectId(ObjectId("507f1f77bcf86cd799439012"))
jsonWriter.writeName("myNumber")
jsonWriter.writeInt64(11223344L)
jsonWriter.writeEndDocument()
jsonWriter.flush()
}
// end-write-bson

// start-custom-conversion
val settings = JsonWriterSettings.builder()
.outputMode(JsonMode.RELAXED)
.objectIdConverter { value, writer ->
writer.writeString(value.toHexString())
}
.dateTimeConverter { value, writer ->
val zonedDateTime = Instant.ofEpochMilli(value).atZone(ZoneOffset.UTC)
writer.writeString(DateTimeFormatter.ISO_DATE_TIME.format(zonedDateTime))
}
.build()

val doc = Document()
.append("_id", ObjectId("507f1f77bcf86cd799439012"))
.append("createdAt", Date.from(Instant.ofEpochMilli(1601499609000L)))
.append("myNumber", 4794261)

println(doc.toJson(settings))
// end-custom-conversion
}
Loading