From c065a6b22238d75bad05872d31123a8e2c590dbb Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 7 Nov 2023 09:37:50 -0700 Subject: [PATCH 1/6] Add Kotlinx serialization JSON dependency Signed-off-by: Mark --- bson-kotlinx/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/bson-kotlinx/build.gradle.kts b/bson-kotlinx/build.gradle.kts index bb9dd42e10b..f98469172cb 100644 --- a/bson-kotlinx/build.gradle.kts +++ b/bson-kotlinx/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { implementation(platform("org.jetbrains.kotlinx:kotlinx-serialization-bom:1.5.0")) implementation("org.jetbrains.kotlinx:kotlinx-serialization-core") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json") api(project(path = ":bson", configuration = "default")) implementation("org.jetbrains.kotlin:kotlin-reflect") From 467c509e6e9e11218b3b4e838e9423cb49085477 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 7 Nov 2023 09:38:13 -0700 Subject: [PATCH 2/6] Add implementation for JsonEncoder in DefaultBsonEncoder Signed-off-by: Mark --- .../org/bson/codecs/kotlinx/BsonEncoder.kt | 72 ++++++++++++++++++- 1 file changed, 69 insertions(+), 3 deletions(-) diff --git a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt index 2e68b992700..ca686d3a0cd 100644 --- a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt +++ b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt @@ -25,12 +25,24 @@ import kotlinx.serialization.descriptors.SerialKind import kotlinx.serialization.descriptors.StructureKind import kotlinx.serialization.encoding.AbstractEncoder import kotlinx.serialization.encoding.CompositeEncoder +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonEncoder +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.double +import kotlinx.serialization.json.int +import kotlinx.serialization.json.long import kotlinx.serialization.modules.SerializersModule import org.bson.BsonValue import org.bson.BsonWriter import org.bson.codecs.BsonValueCodec import org.bson.codecs.EncoderContext +import org.bson.types.Decimal128 import org.bson.types.ObjectId +import java.math.BigDecimal /** * The BsonEncoder interface @@ -62,17 +74,29 @@ internal class DefaultBsonEncoder( private val writer: BsonWriter, override val serializersModule: SerializersModule, private val configuration: BsonConfiguration -) : BsonEncoder, AbstractEncoder() { +) : BsonEncoder, JsonEncoder, AbstractEncoder() { companion object { val validKeyKinds = setOf(PrimitiveKind.STRING, PrimitiveKind.CHAR, SerialKind.ENUM) val bsonValueCodec = BsonValueCodec() + private val DOUBLE_MIN_VALUE = BigDecimal.valueOf(Double.MIN_VALUE) + private val DOUBLE_MAX_VALUE = BigDecimal.valueOf(Double.MAX_VALUE) + private val INT_MIN_VALUE = BigDecimal.valueOf(Int.MIN_VALUE.toLong()) + private val INT_MAX_VALUE = BigDecimal.valueOf(Int.MAX_VALUE.toLong()) + private val LONG_MIN_VALUE = BigDecimal.valueOf(Long.MIN_VALUE) + private val LONG_MAX_VALUE = BigDecimal.valueOf(Long.MAX_VALUE) } private var isPolymorphic = false private var state = STATE.VALUE private var mapState = MapState() private var deferredElementName: String? = null + override val json = Json { + explicitNulls = configuration.explicitNulls + encodeDefaults = configuration.encodeDefaults + classDiscriminator = configuration.classDiscriminator + serializersModule = this@DefaultBsonEncoder.serializersModule + } override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = configuration.encodeDefaults @@ -143,10 +167,10 @@ internal class DefaultBsonEncoder( deferredElementName?.let { if (value != null || configuration.explicitNulls) { encodeName(it) - super.encodeNullableSerializableValue(serializer, value) + super.encodeNullableSerializableValue(serializer, value) } } - ?: super.encodeNullableSerializableValue(serializer, value) + ?: super.encodeNullableSerializableValue(serializer, value) } override fun encodeByte(value: Byte) = encodeInt(value.toInt()) @@ -183,8 +207,50 @@ internal class DefaultBsonEncoder( bsonValueCodec.encode(writer, value, EncoderContext.builder().build()) } + override fun encodeJsonElement(element: JsonElement) = when(element) { + is JsonNull -> encodeNull() + is JsonPrimitive -> { + val content = element.content + when { + element.isString -> encodeString(content) + content == "true" || content == "false" -> + encodeBoolean(content.toBooleanStrict()) + else -> { + val decimal = BigDecimal(content) + when { + decimal.stripTrailingZeros().scale() > 0 && + DOUBLE_MIN_VALUE <= decimal && decimal <= DOUBLE_MAX_VALUE -> + encodeDouble(element.double) + INT_MIN_VALUE <= decimal && decimal <= INT_MAX_VALUE -> + encodeInt(element.int) + LONG_MIN_VALUE <= decimal && decimal <= LONG_MAX_VALUE -> + encodeLong(element.long) + else -> writer.writeDecimal128(Decimal128(decimal)) + } + } + } + } + is JsonObject -> encodeJsonObject(element) + is JsonArray -> encodeJsonArray(element) + } + override fun writer(): BsonWriter = writer + private fun encodeJsonObject(obj: JsonObject) { + writer.writeStartDocument() + obj.forEach { k, v -> + writer.writeName(k) + encodeJsonElement(v) + } + writer.writeEndDocument() + } + + private fun encodeJsonArray(array: JsonArray) { + writer.writeStartArray() + array.forEach(::encodeJsonElement) + writer.writeEndArray() + } + private fun encodeName(value: Any) { writer.writeName(value.toString()) deferredElementName = null From ea548ef1b928a94e919ef5f87ca9db64dfa4368a Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 7 Nov 2023 09:44:23 -0700 Subject: [PATCH 3/6] Add implementation for JsonDecoder in DefaultBsonDecoder Signed-off-by: Mark --- .../org/bson/codecs/kotlinx/BsonDecoder.kt | 96 ++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt index b4cbad3b9dd..1815c6f06c1 100644 --- a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt +++ b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt @@ -27,15 +27,28 @@ import kotlinx.serialization.encoding.AbstractDecoder import kotlinx.serialization.encoding.CompositeDecoder import kotlinx.serialization.encoding.CompositeDecoder.Companion.DECODE_DONE import kotlinx.serialization.encoding.CompositeDecoder.Companion.UNKNOWN_NAME +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonDecoder +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonNull +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonArray +import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.modules.SerializersModule import org.bson.AbstractBsonReader +import org.bson.BsonBinarySubType import org.bson.BsonInvalidOperationException import org.bson.BsonReader import org.bson.BsonType import org.bson.BsonValue +import org.bson.UuidRepresentation import org.bson.codecs.BsonValueCodec import org.bson.codecs.DecoderContext +import org.bson.internal.UuidHelper import org.bson.types.ObjectId +import java.util.Base64 /** * The BsonDecoder interface @@ -58,7 +71,14 @@ internal open class DefaultBsonDecoder( internal val reader: AbstractBsonReader, override val serializersModule: SerializersModule, internal val configuration: BsonConfiguration -) : BsonDecoder, AbstractDecoder() { +) : BsonDecoder, JsonDecoder, AbstractDecoder() { + + override val json = Json { + explicitNulls = configuration.explicitNulls + encodeDefaults = configuration.encodeDefaults + classDiscriminator = configuration.classDiscriminator + serializersModule = this@DefaultBsonDecoder.serializersModule + } private data class ElementMetadata(val name: String, val nullable: Boolean, var processed: Boolean = false) private var elementsMetadata: Array? = null @@ -178,8 +198,82 @@ internal open class DefaultBsonDecoder( override fun decodeObjectId(): ObjectId = readOrThrow({ reader.readObjectId() }, BsonType.OBJECT_ID) override fun decodeBsonValue(): BsonValue = bsonValueCodec.decode(reader, DecoderContext.builder().build()) + + override fun decodeJsonElement(): JsonElement = reader.run { + + if (state == AbstractBsonReader.State.INITIAL || + state == AbstractBsonReader.State.SCOPE_DOCUMENT || + state == AbstractBsonReader.State.TYPE) { + readBsonType() + } + + if (state == AbstractBsonReader.State.NAME) { + // ignore name + skipName() + } + // @formatter:off + return when (currentBsonType) { + BsonType.NULL -> { readNull(); JsonNull } // We have to read null + BsonType.BOOLEAN -> JsonPrimitive(readBoolean()) + BsonType.INT32 -> JsonPrimitive(readInt32()) + BsonType.INT64 -> JsonPrimitive(readInt64()) + BsonType.DOUBLE -> JsonPrimitive(readDouble()) + BsonType.DECIMAL128 -> JsonPrimitive(readDecimal128()) + BsonType.STRING -> JsonPrimitive(readString()) + BsonType.DOCUMENT -> readJsonObject() + BsonType.ARRAY -> readJsonArray() + BsonType.OBJECT_ID -> JsonPrimitive(readObjectId().toHexString()) + BsonType.DATE_TIME -> JsonPrimitive(readDateTime()) + BsonType.TIMESTAMP -> JsonPrimitive(readTimestamp().value) + BsonType.REGULAR_EXPRESSION -> JsonPrimitive(readRegularExpression().pattern) + BsonType.BINARY -> { + val subtype = peekBinarySubType() + val data = readBinaryData().data + when (subtype) { + BsonBinarySubType.UUID_LEGACY.value -> + JsonPrimitive(UuidHelper.decodeBinaryToUuid(data, subtype, UuidRepresentation.JAVA_LEGACY).toString()) + BsonBinarySubType.UUID_STANDARD.value -> + JsonPrimitive(UuidHelper.decodeBinaryToUuid(data, subtype, UuidRepresentation.STANDARD).toString()) + else -> JsonPrimitive(Base64.getEncoder().encodeToString(data)) + } + } + else -> error("unsupported json type: $currentBsonType") + } + // @formatter:on + } + override fun reader(): BsonReader = reader + private fun BsonReader.readJsonObject(): JsonObject { + + readStartDocument() + val obj = buildJsonObject { + var type = readBsonType() + while (type != BsonType.END_OF_DOCUMENT) { + put(readName(), decodeJsonElement()) + type = readBsonType() + } + } + + readEndDocument() + return obj + } + + private fun BsonReader.readJsonArray(): JsonArray { + + readStartArray() + val array = buildJsonArray { + var type = readBsonType() + while (type != BsonType.END_OF_DOCUMENT) { + add(decodeJsonElement()) + type = readBsonType() + } + } + + readEndArray() + return array + } + private inline fun readOrThrow(action: () -> T, bsonType: BsonType): T { return try { action() From dc6b920434393325f1d55d6ebdbee2d4a2650485 Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 7 Nov 2023 10:12:46 -0700 Subject: [PATCH 4/6] Fix writing as an integer when the number is a decimal Signed-off-by: Mark --- .../main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt index ca686d3a0cd..1ee10269f8b 100644 --- a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt +++ b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt @@ -218,9 +218,12 @@ internal class DefaultBsonEncoder( else -> { val decimal = BigDecimal(content) when { - decimal.stripTrailingZeros().scale() > 0 && - DOUBLE_MIN_VALUE <= decimal && decimal <= DOUBLE_MAX_VALUE -> - encodeDouble(element.double) + decimal.stripTrailingZeros().scale() > 0 -> + if (DOUBLE_MIN_VALUE <= decimal && decimal <= DOUBLE_MAX_VALUE) { + encodeDouble(element.double) + } else { + writer.writeDecimal128(Decimal128(decimal)) + } INT_MIN_VALUE <= decimal && decimal <= INT_MAX_VALUE -> encodeInt(element.int) LONG_MIN_VALUE <= decimal && decimal <= LONG_MAX_VALUE -> From fb0a3ab7a37dda9d8fd9a974fb26801b6bafce4e Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 7 Nov 2023 10:22:18 -0700 Subject: [PATCH 5/6] Add test for DataClassWithJsonElement Signed-off-by: Mark --- .../kotlinx/KotlinSerializerCodecTest.kt | 43 +++++++++++++++++++ .../codecs/kotlinx/samples/DataClasses.kt | 6 +++ 2 files changed, 49 insertions(+) diff --git a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt index 146e897c59b..ea63ea6caa4 100644 --- a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt +++ b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/KotlinSerializerCodecTest.kt @@ -19,6 +19,8 @@ import kotlin.test.assertEquals import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.MissingFieldException import kotlinx.serialization.SerializationException +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.put import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.plus import kotlinx.serialization.modules.polymorphic @@ -71,6 +73,7 @@ import org.bson.codecs.kotlinx.samples.DataClassWithEncodeDefault import org.bson.codecs.kotlinx.samples.DataClassWithEnum import org.bson.codecs.kotlinx.samples.DataClassWithEnumMapKey import org.bson.codecs.kotlinx.samples.DataClassWithFailingInit +import org.bson.codecs.kotlinx.samples.DataClassWithJsonElement import org.bson.codecs.kotlinx.samples.DataClassWithMutableList import org.bson.codecs.kotlinx.samples.DataClassWithMutableMap import org.bson.codecs.kotlinx.samples.DataClassWithMutableSet @@ -129,6 +132,46 @@ class KotlinSerializerCodecTest { private val allBsonTypesDocument = BsonDocument.parse(allBsonTypesJson) + @Test + fun testDataClassWithJsonElement() { + + /* + * We need to encode all integer values as longs because the JsonElementSerializer + * doesn't actually use our JsonEncoder instead it uses an inferior + * JsonPrimitiveSerializer and ignores ours altogether encoding all integers as longs + * + * On the other hand, BsonDocument decodes everything as integers unless it's explicitly + * set as a long, and therefore we get a type mismatch + */ + + val expected = """{"value": { + |"char": "c", + |"byte": {"$numberLong": "0"}, + |"short": {"$numberLong": "1"}, + |"int": {"$numberLong": "22"}, + |"long": {"$numberLong": "42"}, + |"float": 4.0, + |"double": 4.2, + |"boolean": true, + |"string": "String" + |}}""".trimMargin() + val dataClass = DataClassWithJsonElement( + buildJsonObject { + put("char", "c") + put("byte", 0) + put("short", 1) + put("int", 22) + put("long", 42L) + put("float", 4.0) + put("double", 4.2) + put("boolean", true) + put("string", "String") + } + ) + + assertRoundTrips(expected, dataClass) + } + @Test fun testDataClassWithSimpleValues() { val expected = diff --git a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt index ea5e3fea3cd..1b897a535c0 100644 --- a/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt +++ b/bson-kotlinx/src/test/kotlin/org/bson/codecs/kotlinx/samples/DataClasses.kt @@ -21,6 +21,7 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Required import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement import org.bson.BsonArray import org.bson.BsonBinary import org.bson.BsonBoolean @@ -50,6 +51,11 @@ import org.bson.codecs.pojo.annotations.BsonProperty import org.bson.codecs.pojo.annotations.BsonRepresentation import org.bson.types.ObjectId +@Serializable +data class DataClassWithJsonElement( + val value: JsonElement +) + @Serializable data class DataClassWithSimpleValues( val char: Char, From 94ffd5304844ec9498e2fa9b3aea6c9d5545602c Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 8 Nov 2023 09:30:06 -0700 Subject: [PATCH 6/6] Adjust encode and decode functions to comply with checks Signed-off-by: Mark --- .../org/bson/codecs/kotlinx/BsonDecoder.kt | 65 +++++++++++-------- .../org/bson/codecs/kotlinx/BsonEncoder.kt | 50 +++++++------- 2 files changed, 63 insertions(+), 52 deletions(-) diff --git a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt index 1815c6f06c1..0e46dc4211f 100644 --- a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt +++ b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonDecoder.kt @@ -199,6 +199,7 @@ internal open class DefaultBsonDecoder( override fun decodeObjectId(): ObjectId = readOrThrow({ reader.readObjectId() }, BsonType.OBJECT_ID) override fun decodeBsonValue(): BsonValue = bsonValueCodec.decode(reader, DecoderContext.builder().build()) + @Suppress("ComplexMethod") override fun decodeJsonElement(): JsonElement = reader.run { if (state == AbstractBsonReader.State.INITIAL || @@ -211,29 +212,37 @@ internal open class DefaultBsonDecoder( // ignore name skipName() } + // @formatter:off return when (currentBsonType) { - BsonType.NULL -> { readNull(); JsonNull } // We have to read null - BsonType.BOOLEAN -> JsonPrimitive(readBoolean()) - BsonType.INT32 -> JsonPrimitive(readInt32()) - BsonType.INT64 -> JsonPrimitive(readInt64()) - BsonType.DOUBLE -> JsonPrimitive(readDouble()) - BsonType.DECIMAL128 -> JsonPrimitive(readDecimal128()) - BsonType.STRING -> JsonPrimitive(readString()) BsonType.DOCUMENT -> readJsonObject() BsonType.ARRAY -> readJsonArray() - BsonType.OBJECT_ID -> JsonPrimitive(readObjectId().toHexString()) - BsonType.DATE_TIME -> JsonPrimitive(readDateTime()) - BsonType.TIMESTAMP -> JsonPrimitive(readTimestamp().value) - BsonType.REGULAR_EXPRESSION -> JsonPrimitive(readRegularExpression().pattern) + BsonType.NULL -> JsonPrimitive(decodeNull()) + BsonType.STRING -> JsonPrimitive(decodeString()) + BsonType.BOOLEAN -> JsonPrimitive(decodeBoolean()) + BsonType.INT32 -> JsonPrimitive(decodeInt()) + BsonType.INT64 -> JsonPrimitive(decodeLong()) + BsonType.DOUBLE -> JsonPrimitive(decodeDouble()) + BsonType.DECIMAL128 -> JsonPrimitive(reader.readDecimal128()) + BsonType.OBJECT_ID -> JsonPrimitive(decodeObjectId().toHexString()) + BsonType.DATE_TIME -> JsonPrimitive(reader.readDateTime()) + BsonType.TIMESTAMP -> JsonPrimitive(reader.readTimestamp().value) BsonType.BINARY -> { - val subtype = peekBinarySubType() - val data = readBinaryData().data + val subtype = reader.peekBinarySubType() + val data = reader.readBinaryData().data when (subtype) { - BsonBinarySubType.UUID_LEGACY.value -> - JsonPrimitive(UuidHelper.decodeBinaryToUuid(data, subtype, UuidRepresentation.JAVA_LEGACY).toString()) - BsonBinarySubType.UUID_STANDARD.value -> - JsonPrimitive(UuidHelper.decodeBinaryToUuid(data, subtype, UuidRepresentation.STANDARD).toString()) + BsonBinarySubType.UUID_LEGACY.value -> JsonPrimitive( + UuidHelper.decodeBinaryToUuid( + data, subtype, + UuidRepresentation.JAVA_LEGACY + ).toString() + ) + BsonBinarySubType.UUID_STANDARD.value -> JsonPrimitive( + UuidHelper.decodeBinaryToUuid( + data, subtype, + UuidRepresentation.STANDARD + ).toString() + ) else -> JsonPrimitive(Base64.getEncoder().encodeToString(data)) } } @@ -244,33 +253,33 @@ internal open class DefaultBsonDecoder( override fun reader(): BsonReader = reader - private fun BsonReader.readJsonObject(): JsonObject { + private fun readJsonObject(): JsonObject { - readStartDocument() + reader.readStartDocument() val obj = buildJsonObject { - var type = readBsonType() + var type = reader.readBsonType() while (type != BsonType.END_OF_DOCUMENT) { - put(readName(), decodeJsonElement()) - type = readBsonType() + put(reader.readName(), decodeJsonElement()) + type = reader.readBsonType() } } - readEndDocument() + reader.readEndDocument() return obj } - private fun BsonReader.readJsonArray(): JsonArray { + private fun readJsonArray(): JsonArray { - readStartArray() + reader.readStartArray() val array = buildJsonArray { - var type = readBsonType() + var type = reader.readBsonType() while (type != BsonType.END_OF_DOCUMENT) { add(decodeJsonElement()) - type = readBsonType() + type = reader.readBsonType() } } - readEndArray() + reader.readEndArray() return array } diff --git a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt index 1ee10269f8b..9de6a5fd4e0 100644 --- a/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt +++ b/bson-kotlinx/src/main/kotlin/org/bson/codecs/kotlinx/BsonEncoder.kt @@ -209,36 +209,38 @@ internal class DefaultBsonEncoder( override fun encodeJsonElement(element: JsonElement) = when(element) { is JsonNull -> encodeNull() - is JsonPrimitive -> { - val content = element.content - when { - element.isString -> encodeString(content) - content == "true" || content == "false" -> - encodeBoolean(content.toBooleanStrict()) - else -> { - val decimal = BigDecimal(content) - when { - decimal.stripTrailingZeros().scale() > 0 -> - if (DOUBLE_MIN_VALUE <= decimal && decimal <= DOUBLE_MAX_VALUE) { - encodeDouble(element.double) - } else { - writer.writeDecimal128(Decimal128(decimal)) - } - INT_MIN_VALUE <= decimal && decimal <= INT_MAX_VALUE -> - encodeInt(element.int) - LONG_MIN_VALUE <= decimal && decimal <= LONG_MAX_VALUE -> - encodeLong(element.long) - else -> writer.writeDecimal128(Decimal128(decimal)) - } - } - } - } + is JsonPrimitive -> encodeJsonPrimitive(element) is JsonObject -> encodeJsonObject(element) is JsonArray -> encodeJsonArray(element) } override fun writer(): BsonWriter = writer + private fun encodeJsonPrimitive(primitive: JsonPrimitive) { + val content = primitive.content + when { + primitive.isString -> encodeString(content) + content == "true" || content == "false" -> + encodeBoolean(content.toBooleanStrict()) + else -> { + val decimal = BigDecimal(content) + when { + decimal.stripTrailingZeros().scale() > 0 -> + if (DOUBLE_MIN_VALUE <= decimal && decimal <= DOUBLE_MAX_VALUE) { + encodeDouble(primitive.double) + } else { + writer.writeDecimal128(Decimal128(decimal)) + } + INT_MIN_VALUE <= decimal && decimal <= INT_MAX_VALUE -> + encodeInt(primitive.int) + LONG_MIN_VALUE <= decimal && decimal <= LONG_MAX_VALUE -> + encodeLong(primitive.long) + else -> writer.writeDecimal128(Decimal128(decimal)) + } + } + } + } + private fun encodeJsonObject(obj: JsonObject) { writer.writeStartDocument() obj.forEach { k, v ->