Skip to content
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

DOCSP-42666: serialization #39

Merged
merged 6 commits into from
Aug 21, 2024
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
1 change: 1 addition & 0 deletions snooty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ api = "https://mongodb.github.io/mongo-java-driver/{+version-number+}/apidocs/mo
java-api = "https://mongodb.github.io/mongo-java-driver/{+version-number+}"
core-api = "{+java-api+}/apidocs/mongodb-driver-core"
kotlin-docs = "https://kotlinlang.org"
serialization-version = "1.5.1"
9 changes: 8 additions & 1 deletion source/data-formats.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Specialized Data Formats
:titlesonly:
:maxdepth: 1

/data-formats/serialization
/data-formats/bson
/data-formats/time-series

Expand All @@ -30,7 +31,13 @@ Specialized Data Formats
Overview
--------

You can use several types of specialized document data formats in your {+driver-short+}
The {+driver-short+} supports the ``kotlinx.serialization`` library for
serializing and deserializing {+language+} objects. You can use this
library to convert between MongoDB documents and different data formats
in your application. To learn more about serialization, see the
:ref:`kotlin-sync-serialization` guide.

You can use several types of specialized document data formats in your
application. To learn how to work with these data formats, see the following
sections:

Expand Down
302 changes: 302 additions & 0 deletions source/data-formats/serialization.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
.. _kotlin-sync-serialization:

====================
Kotlin Serialization
====================

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

.. meta::
:keywords: code example, data model, conversion, polymorphism

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

Overview
--------

You can use the ``kotlinx.serialization`` library when
serializing and deserializing {+language+} objects in your application.

The driver provides an efficient ``Bson`` serializer that you can use with
classes marked with the ``@Serializable`` annotation to handle the
serialization of {+language+} objects to BSON data.

Although you can use the {+language+} serialization ``Json``
library, the ``Json`` serializer does *not* directly support BSON value types such
as ``ObjectId``. You must provide a custom serializer that can handle the
conversion between BSON and JSON.

We recommend installing the ``bson-kotlinx`` library to support
custom codecs that have configurations to encode defaults, nulls, and
define class discriminators.

.. TODO fix the link in the following note

.. note::

To learn how to use the ``Codec`` interface instead of the
``kotlinx.serialization`` library to specify custom
serialization behavior, see the Codecs guide.

You might choose to use {+language+} serialization if you
are already familiar with the library or if you prefer
to use an idiomatic approach.

Supported Types
~~~~~~~~~~~~~~~

The {+driver-short+} supports the following types:

- All {+language+} types that are supported by the ``kotlinx.serialization`` library
- All :manual:`BSON types </reference/bson-types>`

.. _kotlin-sync-add-serialization:

Add the Serialization Dependencies to Your Project
--------------------------------------------------

You must install the official {+language+} serialization library,
``kotlinx.serialization``, to serialize and deserialize data in your
application. To learn more about this library, see the
`kotlinx.serialization GitHub repository
<https://github.com/Kotlin/kotlinx.serialization>`__.

Select from the following tabs to see how to add the serialization
dependencies to your project by using either :guilabel:`Gradle` or
:guilabel:`Maven`:

.. tabs::

.. tab::
:tabid: Gradle

If you are using `Gradle <https://gradle.org/>`__ to manage your
dependencies, add the following to your ``build.gradle.kts``
dependencies list:

.. include:: /includes/data-formats/serialization-gradle-versioned.rst

.. tab::
:tabid: Maven

If you are using `Maven <https://maven.apache.org/>`__ to manage your
dependencies, add the following to your ``pom.xml`` dependencies
list:

.. include:: /includes/data-formats/serialization-maven-versioned.rst

.. note:: bson-kotlin Dependency

You can also optionally install the ``bson-kotlin`` dependency
through the default codec registry. This dependency uses reflection
and the codec registry to support {+language+} data classes, but it does
not support certain POJO annotations such as ``BsonDiscriminator``,
``BsonExtraElements``, and ``BsonConstructor``. To learn more, see
the `bson-kotlin API documentation <{+java-api+}/apidocs/bson-kotlin/index.html>`__.

Generally, we recommend that you install and use the faster
``bson-kotlinx`` library for codec configuration.

.. _kotlin-sync-data-class-annotation:

Annotate Data Classes
---------------------

To declare a class as serializable, annotate your {+language+} data
classes with the ``@Serializable`` annotation.

You can use your data classes in your code as you use any other data
class after you mark them as serializable. The {+driver-short+} and the
{+language+} serialization framework handle BSON serialization and
deserialization.

This example shows a sample data class with the following annotations:

- ``@Serializable``: Marks the class as serializable.
- ``@SerialName``: Specifies the name of the ``id`` and ``manufacturer`` properties
in the BSON document. This annotation can be used in place of the ``@BsonId`` and
``@BsonProperty`` annotations, which are unsupported in serializable classes.
- ``@Contextual``: Marks the BSON ``id`` property to use the built-in
``ObjectIdSerializer``. This annotation is required for the driver to
serialize BSON types correctly.

.. code-block:: kotlin

@Serializable
data class PaintOrder(
@SerialName("_id") // Use instead of @BsonId
@Contextual val id: ObjectId?,
val color: String,
val qty: Int,
@SerialName("brand")
val manufacturer: String = "Grumbacher" // Use instead of @BsonProperty
)

.. note:: POJO Annotations Limitation

You cannot use annotations from the
``org.bson.codecs.pojo.annotations`` package on a data class marked
with the ``@Serializable`` annotation.

To learn more about serializable classes and available annotations,
see `Serializable classes
<https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md#serializable-classes>`__
in the ``kotlinx.serialization`` library documentation.

Custom Serializer Example
~~~~~~~~~~~~~~~~~~~~~~~~~

You can create a custom serializer to handle how your data is
represented in BSON. The {+driver-short+} uses the ``KSerializer``
interface from the ``kotlinx.serialization`` library to implement custom
serializers. You can specify the custom serializer as the parameter to
the ``@Serializable`` annotation for a specific field.

The following example shows how to create a custom
``KSerializer`` instance to convert a ``kotlinx.datetime.Instant`` to a
``BsonDateTime``:

.. literalinclude:: /includes/data-formats/serialization.kt
:language: kotlin
:start-after: start-kserializer
:end-before: end-kserializer
:dedent:

The following code shows the ``PaintOrder`` data class in which the
``orderDate`` field has an annotation that specifies the custom
serializer class defined in the preceding code:

.. literalinclude:: /includes/data-formats/serialization.kt
:language: kotlin
:start-after: start-ks-dataclass
:end-before: end-ks-dataclass
:emphasize-lines: 5
:dedent:

To learn more about the methods and classes mentioned in this section,
see the following API documentation:

- `KSerializer
<{+kotlin-docs+}/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/>`__
in the {+language+} documentation

- `Instant
<{+kotlin-docs+}/api/kotlinx-datetime/kotlinx-datetime/kotlinx.datetime/-instant/>`__ in the {+language+} documentation

- `BsonEncoder
<{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-encoder/index.html>`__

- `BsonDecoder
<{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-decoder/index.html>`__

.. _kotlin-sync-custom-codec:

Customize the Serializer Configuration
--------------------------------------

You can use the ``KotlinSerializerCodec`` class from the ``org.bson.codecs.kotlinx``
package to create a codec for your ``@Serializable`` data classes and
customize what the driver stores in MongoDB.

Use the ``BsonConfiguration`` class to define the configuration,
which can include whether to encode defaults, encode nulls, or define
class discriminators.

To create a custom codec, your project must have the ``bson-kotlinx``
dependency. See the :ref:`kotlin-sync-add-serialization` section of this
guide for installation instructions.

You can define your codec by using the `KotlinSerializerCodec.create()
<{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/index.html>`__
method, then you can add the codec to the registry.

Custom Codec Example
~~~~~~~~~~~~~~~~~~~~

The following example shows how to create a codec by using the
``KotlinSerializerCodec.create()`` method and then configure it to *not*
encode defaults:

.. literalinclude:: /includes/data-formats/serialization.kt
:language: kotlin
:start-after: start-codec
:end-before: end-codec
:dedent:

To learn more about the methods and classes mentioned in this section,
see the following API documentation:

- `KotlinSerializerCodec
<{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/index.html>`__

- `KotlinSerializerCodec.create()
<{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/create.html>`__

- `BsonConfiguration
<{+java-api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-configuration/index.html>`__

.. _kotlin-sync-polymorphic:

Polymorphic Serialization
-------------------------

The {+driver-short+} natively supports serialization and deserialization
of polymorphic classes. When you mark a sealed interface and data
classes that inherit that interface with the ``@Serializable``
annotation, the driver uses a ``KSerializer`` implementation to handle
conversion of your types to and from BSON.

When you insert an instance of a polymorphic data class into MongoDB,
the driver adds the field ``_t``, the
discriminator field. The value of this field is the data class name.

Polymorphic Data Classes Example
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The following example creates an interface and two data classes that
inherit that interface. In the data classes, the ``id`` field is marked
with the annotations described in the
:ref:`kotlin-sync-data-class-annotation` section:

.. literalinclude:: /includes/data-formats/serialization.kt
:language: kotlin
:start-after: start-poly-classes
:end-before: end-poly-classes
:dedent:

Then, you can perform operations with data classes as usual. The
following example parametrizes the collection with the ``Person``
interface, then performs operations with the polymorphic classes
``Teacher`` and ``Student``. When you retrieve documents, the driver
automatically detects the type based on the discriminator value and
deserializes them accordingly.

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

.. input:: /includes/data-formats/serialization.kt
:language: kotlin
:start-after: start-poly-operations
:end-before: end-poly-operations
:dedent:

.. output::
:language: console

Retrieving by using data classes
Teacher(id=..., name=Vivian Lee, department=History)
Student(id=..., name=Kate Parker, grade=10)

Retrieving by using Person interface
Teacher(id=..., name=Vivian Lee, department=History)
Student(id=..., name=Kate Parker, grade=10)

Retrieving as Document type
Document{{_id=..., _t=Teacher, name=Vivian Lee, department=History}}
Document{{_id=..., _t=Student, name=Kate Parker, grade=10}}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.. code-block:: kotlin
:caption: build.gradle.kts

implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:{+serialization-version+}")
implementation("org.mongodb:bson-kotlinx:{+full-version+}")
13 changes: 13 additions & 0 deletions source/includes/data-formats/serialization-maven-versioned.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
.. code-block:: xml
:caption: pom.xml

<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-serialization-core</artifactId>
<version>{+serialization-version+}</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>bson-kotlinx</artifactId>
<version>{+full-version+}</version>
</dependency>
Loading
Loading