diff --git a/source/data-formats.txt b/source/data-formats.txt index b1cad2cd..c553d5af 100644 --- a/source/data-formats.txt +++ b/source/data-formats.txt @@ -25,8 +25,7 @@ Data Formats Data Classes BSON Time Series - -.. TODO: /data-formats/extended-json + Extended JSON Overview -------- @@ -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. diff --git a/source/data-formats/extended-json.txt b/source/data-formats/extended-json.txt new file mode 100644 index 00000000..9795a17d --- /dev/null +++ b/source/data-formats/extended-json.txt @@ -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>`__ \ No newline at end of file diff --git a/source/includes/data-formats/ext-json.kt b/source/includes/data-formats/ext-json.kt new file mode 100644 index 00000000..fe48d7ab --- /dev/null +++ b/source/includes/data-formats/ext-json.kt @@ -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 +} \ No newline at end of file