Skip to content

Commit 05b2ee2

Browse files
committed
DOCSP-36217: polymorphic serialization (#163)
* DOCSP-36217: polymorphic serialization * wip * wip * wip * fix error in codectest * MM PR fixes 1 (cherry picked from commit b5a5af9)
1 parent 75a82d3 commit 05b2ee2

File tree

10 files changed

+171
-58
lines changed

10 files changed

+171
-58
lines changed

examples/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ dependencies {
2828
implementation("com.github.luben:zstd-jni:1.5.5-4")
2929
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.5.1")
3030
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")
31-
implementation("org.mongodb:bson-kotlinx:4.10.0-alpha1")
31+
implementation("org.mongodb:bson-kotlinx:5.1.1")
3232
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
3333
}
3434

examples/src/test/kotlin/CodecTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ internal class CodecTest {
6666

6767
init {
6868
powerStatusCodec = registry[PowerStatus::class.java]
69-
integerCodec = registry.get(Int::class.java)
69+
integerCodec = IntegerCodec()
7070
}
7171

7272
override fun encode(writer: BsonWriter, value: Monolight, encoderContext: EncoderContext) {

examples/src/test/kotlin/KotlinXSerializationTest.kt

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11

2+
import com.mongodb.client.model.Filters
23
import com.mongodb.client.model.Filters.eq
34
import com.mongodb.kotlin.client.coroutine.MongoClient
45
import config.getConfig
@@ -35,6 +36,7 @@ import org.junit.jupiter.api.Assertions.assertFalse
3536
import org.junit.jupiter.api.Test
3637
import org.junit.jupiter.api.TestInstance
3738
import java.util.Date
39+
import kotlin.test.assertTrue
3840

3941
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
4042
internal class KotlinXSerializationTest {
@@ -178,5 +180,67 @@ internal class KotlinXSerializationTest {
178180
collection.drop()
179181
}
180182

183+
// :snippet-start: polymorphic-dataclasses
184+
@Serializable
185+
sealed interface Person {
186+
val name: String
187+
}
188+
189+
@Serializable
190+
data class Student(
191+
@Contextual
192+
@SerialName("_id")
193+
val id: ObjectId,
194+
override val name: String,
195+
val grade: Int,
196+
) : Person
197+
198+
@Serializable
199+
data class Teacher(
200+
@Contextual
201+
@SerialName("_id")
202+
val id: ObjectId,
203+
override val name: String,
204+
val department: String,
205+
) : Person
206+
// :snippet-end:
207+
208+
@Test
209+
fun polymorphicSerializationTest() = runBlocking {
210+
211+
// :snippet-start: polymorphic-example
212+
val collection = database.getCollection<Person>("school")
213+
214+
val teacherDoc = Teacher(ObjectId(), "Vivian Lee", "History")
215+
val studentDoc = Student(ObjectId(), "Kate Parker", 10)
216+
217+
collection.insertOne(teacherDoc)
218+
collection.insertOne(studentDoc)
219+
220+
println("Retrieving by using data classes")
221+
collection.withDocumentClass<Teacher>()
222+
.find(Filters.exists("department"))
223+
.first().also { println(it) }
224+
225+
collection.withDocumentClass<Student>()
226+
.find(Filters.exists("grade"))
227+
.first().also { println(it) }
228+
229+
println("\nRetrieving by using Person interface")
230+
val resultsFlow = collection.withDocumentClass<Person>().find()
231+
resultsFlow.collect { println(it) }
232+
233+
println("\nRetrieving as Document type")
234+
val resultsDocFlow = collection.withDocumentClass<Document>().find()
235+
resultsDocFlow.collect { println(it) }
236+
// :snippet-end:
237+
238+
if (resultsFlow != null) {
239+
assertTrue(resultsFlow.firstOrNull() is Teacher)
240+
}
241+
242+
collection.drop()
243+
}
244+
181245
}
182246

examples/src/test/kotlin/TlsTest.kt

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11

22
import com.mongodb.ConnectionString
33
import com.mongodb.MongoClientSettings
4-
import com.mongodb.connection.netty.NettyStreamFactoryFactory
54
import com.mongodb.kotlin.client.coroutine.MongoClient
65
import config.getConfig
76
import io.netty.handler.ssl.SslContextBuilder
@@ -87,25 +86,5 @@ internal class TlsTest {
8786
assertEquals(sslContext, settings.sslSettings.context)
8887
}
8988

90-
@Test
91-
fun nettyTlsConfigurationTest() {
92-
// :snippet-start: netty-tls-configuration
93-
val sslContext = SslContextBuilder.forClient()
94-
.sslProvider(SslProvider.OPENSSL)
95-
.build()
96-
val settings = MongoClientSettings.builder()
97-
.applyToSslSettings { builder -> builder.enabled(true) }
98-
.streamFactoryFactory(
99-
NettyStreamFactoryFactory.builder()
100-
.sslContext(sslContext)
101-
.build()
102-
)
103-
.build()
104-
val mongoClient = MongoClient.create(settings)
105-
// :snippet-end:
106-
mongoClient.close()
107-
assertEquals(true, settings.sslSettings.isEnabled)
108-
assertEquals(true, settings.streamFactoryFactory is NettyStreamFactoryFactory)
109-
}
11089
}
11190
// :replace-end:

source/examples/generated/CodecTest.snippet.example-codec-2.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ class MonolightCodec(registry: CodecRegistry) : Codec<Monolight> {
44

55
init {
66
powerStatusCodec = registry[PowerStatus::class.java]
7-
integerCodec = registry.get(Int::class.java)
7+
integerCodec = IntegerCodec()
88
}
99

1010
override fun encode(writer: BsonWriter, value: Monolight, encoderContext: EncoderContext) {
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
@Serializable
2+
sealed interface Person {
3+
val name: String
4+
}
5+
6+
@Serializable
7+
data class Student(
8+
@Contextual
9+
@SerialName("_id")
10+
val id: ObjectId,
11+
override val name: String,
12+
val grade: Int,
13+
) : Person
14+
15+
@Serializable
16+
data class Teacher(
17+
@Contextual
18+
@SerialName("_id")
19+
val id: ObjectId,
20+
override val name: String,
21+
val department: String,
22+
) : Person
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
val collection = database.getCollection<Person>("school")
2+
3+
val teacherDoc = Teacher(ObjectId(), "Vivian Lee", "History")
4+
val studentDoc = Student(ObjectId(), "Kate Parker", 10)
5+
6+
collection.insertOne(teacherDoc)
7+
collection.insertOne(studentDoc)
8+
9+
println("Retrieving by using data classes")
10+
collection.withDocumentClass<Teacher>()
11+
.find(Filters.exists("department"))
12+
.first().also { println(it) }
13+
14+
collection.withDocumentClass<Student>()
15+
.find(Filters.exists("grade"))
16+
.first().also { println(it) }
17+
18+
println("\nRetrieving by using Person interface")
19+
val resultsFlow = collection.withDocumentClass<Person>().find()
20+
resultsFlow.collect { println(it) }
21+
22+
println("\nRetrieving as Document type")
23+
val resultsDocFlow = collection.withDocumentClass<Document>().find()
24+
resultsDocFlow.collect { println(it) }

source/fundamentals/connection/tls.txt

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -239,36 +239,6 @@ object to the builder in the ``applyToSslSettings()`` lambda:
239239
.. literalinclude:: /examples/generated/TlsTest.snippet.custom-tls-configuration.kt
240240
:language: kotlin
241241

242-
Customize TLS/SSL Configuration through the Netty SslContext
243-
------------------------------------------------------------
244-
245-
If you use the driver with `Netty <https://netty.io/>`__ for network IO,
246-
you have an option to plug an alternative TLS/SSL protocol implementation
247-
provided by Netty.
248-
249-
.. code-block:: kotlin
250-
:copyable: true
251-
252-
import com.mongodb.MongoClientSettings
253-
import com.mongodb.client.MongoClient
254-
import com.mongodb.connection.netty.NettyStreamFactoryFactory
255-
import io.netty.handler.ssl.SslContext
256-
import io.netty.handler.ssl.SslContextBuilder
257-
import io.netty.handler.ssl.SslProvider
258-
259-
.. note::
260-
261-
The driver tests with Netty version ``{+nettyVersion+}``
262-
263-
To instruct the driver to use `io.netty.handler.ssl.SslContext <https://netty.io/4.1/api/io/netty/handler/ssl/SslContext.html>`__,
264-
use the `NettyStreamFactoryFactory.Builder.sslContext <{+api+}/apidocs/mongodb-driver-core/com/mongodb/connection/netty/NettyStreamFactoryFactory.Builder.html#sslContext(io.netty.handler.ssl.SslContext)>`__
265-
method. See the method documentation for details about the different `io.netty.handler.ssl.SslProvider <https://netty.io/4.1/api/io/netty/handler/ssl/SslProvider.html>`__
266-
variants the driver supports and the implications of using them.
267-
268-
.. literalinclude:: /examples/generated/TlsTest.snippet.netty-tls-configuration.kt
269-
:language: kotlin
270-
271-
272242
Online Certificate Status Protocol (OCSP)
273243
-----------------------------------------
274244

source/fundamentals/data-formats/serialization.txt

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Kotlin Serialization
99
:values: reference
1010

1111
.. meta::
12-
:keywords: code example, data model, conversion
12+
:keywords: code example, data model, conversion, polymorphism
1313

1414
.. contents:: On this page
1515
:local:
@@ -98,6 +98,8 @@ dependencies to your project by using the :guilabel:`Gradle` and
9898
<version>{+full-version+}</version>
9999
</dependency>
100100

101+
.. _kotlin-data-class-annotation:
102+
101103
Annotate Data Classes
102104
---------------------
103105

@@ -246,3 +248,56 @@ see the following API documentation:
246248
- `KotlinSerializerCodec.create() <{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-kotlin-serializer-codec/-companion/create.html>`__
247249
- `BsonConfiguration <{+api+}/apidocs/bson-kotlinx/bson-kotlinx/org.bson.codecs.kotlinx/-bson-configuration/index.html>`__
248250

251+
.. _kotlin-polymorphic:
252+
253+
Polymorphic Serialization
254+
-------------------------
255+
256+
The {+driver-short+} natively supports serialization and deserialization
257+
of polymorphic classes. When you mark a sealed interface and data
258+
classes that inherit that interface with the ``@Serializable``
259+
annotation, the driver uses a ``KSerializer`` implementation to handle
260+
conversion of your types to and from BSON.
261+
262+
When you insert an instance of a polymorphic data class into MongoDB,
263+
the driver adds the field ``_t``, the
264+
discriminator field. The value of this field is the data class name.
265+
266+
Polymorphic Data Classes Example
267+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
268+
269+
The following example creates an interface and two data classes that
270+
inherit that interface. In the data classes, the ``id`` field is marked
271+
with the annotations described in the
272+
:ref:`kotlin-data-class-annotation` section:
273+
274+
.. literalinclude:: /examples/generated/KotlinXSerializationTest.snippet.polymorphic-dataclasses.kt
275+
:language: kotlin
276+
277+
Then, you can perform operations with data classes as usual. The
278+
following example parametrizes the collection with the ``Person``
279+
interface, then performs operations with the polymorphic classes
280+
``Teacher`` and ``Student``. When you retrieve documents, the driver
281+
automatically detects the type based on the discriminator value and
282+
deserializes them accordingly.
283+
284+
.. io-code-block::
285+
:copyable: true
286+
287+
.. input:: /examples/generated/KotlinXSerializationTest.snippet.polymorphic-example.kt
288+
:language: kotlin
289+
290+
.. output::
291+
:language: console
292+
293+
Retrieving by using data classes
294+
Teacher(id=..., name=Vivian Lee, department=History)
295+
Student(id=..., name=Kate Parker, grade=10)
296+
297+
Retrieving by using Person interface
298+
Teacher(id=..., name=Vivian Lee, department=History)
299+
Student(id=..., name=Kate Parker, grade=10)
300+
301+
Retrieving as Document type
302+
Document{{_id=..., _t=Teacher, name=Vivian Lee, department=History}}
303+
Document{{_id=..., _t=Student, name=Kate Parker, grade=10}}

source/whats-new.txt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,8 @@ Improvements in 5.1
8282
New Features in 5.1
8383
~~~~~~~~~~~~~~~~~~~
8484

85-
- Support for polymorphic serialization.
86-
87-
.. TODO add polymorphic serialization content
85+
- Support for polymorphic serialization. To learn more, see the
86+
:ref:`kotlin-polymorphic` section of the Kotlin Serialization guide.
8887

8988
- Introduces the ``serverMonitoringMode`` connection URI option. To
9089
learn more, see the :ref:`connection-options` guide.

0 commit comments

Comments
 (0)