-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Added kotlinx.json JsonElement serialization support #1459
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
Changes from 2 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
7e4661c
Added kotlinx.json JsonElement serialization support
rozza 3635e92
Spotless style fix
rozza d17c4d6
Delegate to the JsonBsonEncoder
rozza 78126dc
Added explicit suppress pom metadata warnings
rozza ec24312
Simpfly null tests and add test for nullable JsonElements
rozza 76ff32b
Merge branch 'master' into JAVA-5239
rozza a7fdaed
Add dataClassWithAllSupportedJsonTypes encodes to example
rozza 5e08826
Merge branch 'master' into JAVA-5239
rozza 5b08165
Merge branch 'master' into JAVA-5239
rozza File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ 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.encoding.Decoder | ||
import kotlinx.serialization.modules.SerializersModule | ||
import org.bson.AbstractBsonReader | ||
import org.bson.BsonInvalidOperationException | ||
|
@@ -36,6 +37,10 @@ import org.bson.BsonType | |
import org.bson.BsonValue | ||
import org.bson.codecs.BsonValueCodec | ||
import org.bson.codecs.DecoderContext | ||
import org.bson.codecs.kotlinx.BsonDecoder.Companion.createBsonArrayDecoder | ||
import org.bson.codecs.kotlinx.BsonDecoder.Companion.createBsonDocumentDecoder | ||
import org.bson.codecs.kotlinx.BsonDecoder.Companion.createBsonMapDecoder | ||
import org.bson.codecs.kotlinx.BsonDecoder.Companion.createBsonPolymorphicDecoder | ||
import org.bson.internal.NumberCodecHelper | ||
import org.bson.internal.StringCodecHelper | ||
import org.bson.types.ObjectId | ||
|
@@ -45,34 +50,93 @@ import org.bson.types.ObjectId | |
* | ||
* For custom serialization handlers | ||
*/ | ||
public sealed interface BsonDecoder { | ||
@ExperimentalSerializationApi | ||
internal sealed interface BsonDecoder : Decoder, CompositeDecoder { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As this is sealed there is no need for it to have been public - as all implementations are private. So its effectively private. |
||
|
||
/** Factory helper for creating concrete BsonDecoder implementations */ | ||
companion object { | ||
|
||
@Suppress("SwallowedException") | ||
private val hasJsonDecoder: Boolean by lazy { | ||
try { | ||
Class.forName("kotlinx.serialization.json.JsonDecoder") | ||
true | ||
} catch (e: ClassNotFoundException) { | ||
false | ||
} | ||
} | ||
|
||
fun createBsonDecoder( | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
): BsonDecoder { | ||
return if (hasJsonDecoder) JsonBsonDecoderImpl(reader, serializersModule, configuration) | ||
else BsonDecoderImpl(reader, serializersModule, configuration) | ||
} | ||
|
||
fun createBsonArrayDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
): BsonArrayDecoder { | ||
return if (hasJsonDecoder) JsonBsonArrayDecoder(descriptor, reader, serializersModule, configuration) | ||
else BsonArrayDecoder(descriptor, reader, serializersModule, configuration) | ||
} | ||
|
||
fun createBsonDocumentDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
): BsonDocumentDecoder { | ||
return if (hasJsonDecoder) JsonBsonDocumentDecoder(descriptor, reader, serializersModule, configuration) | ||
else BsonDocumentDecoder(descriptor, reader, serializersModule, configuration) | ||
} | ||
|
||
fun createBsonPolymorphicDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
): BsonPolymorphicDecoder { | ||
return if (hasJsonDecoder) JsonBsonPolymorphicDecoder(descriptor, reader, serializersModule, configuration) | ||
else BsonPolymorphicDecoder(descriptor, reader, serializersModule, configuration) | ||
} | ||
|
||
fun createBsonMapDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
): BsonMapDecoder { | ||
return if (hasJsonDecoder) JsonBsonMapDecoder(descriptor, reader, serializersModule, configuration) | ||
else BsonMapDecoder(descriptor, reader, serializersModule, configuration) | ||
} | ||
} | ||
|
||
/** @return the decoded ObjectId */ | ||
public fun decodeObjectId(): ObjectId | ||
fun decodeObjectId(): ObjectId | ||
/** @return the decoded BsonValue */ | ||
public fun decodeBsonValue(): BsonValue | ||
|
||
/** @return the BsonReader */ | ||
public fun reader(): BsonReader | ||
fun decodeBsonValue(): BsonValue | ||
} | ||
|
||
@ExperimentalSerializationApi | ||
internal open class DefaultBsonDecoder( | ||
internal val reader: AbstractBsonReader, | ||
@OptIn(ExperimentalSerializationApi::class) | ||
internal sealed class AbstractBsonDecoder( | ||
val reader: AbstractBsonReader, | ||
override val serializersModule: SerializersModule, | ||
internal val configuration: BsonConfiguration | ||
val configuration: BsonConfiguration | ||
) : BsonDecoder, AbstractDecoder() { | ||
|
||
private data class ElementMetadata(val name: String, val nullable: Boolean, var processed: Boolean = false) | ||
private var elementsMetadata: Array<ElementMetadata>? = null | ||
private var currentIndex: Int = UNKNOWN_INDEX | ||
|
||
companion object { | ||
val validKeyKinds = setOf(PrimitiveKind.STRING, PrimitiveKind.CHAR, SerialKind.ENUM) | ||
|
||
val bsonValueCodec = BsonValueCodec() | ||
const val UNKNOWN_INDEX = -10 | ||
val validKeyKinds = setOf(PrimitiveKind.STRING, PrimitiveKind.CHAR, SerialKind.ENUM) | ||
|
||
fun validateCurrentBsonType( | ||
reader: AbstractBsonReader, | ||
reader: BsonReader, | ||
expectedType: BsonType, | ||
descriptor: SerialDescriptor, | ||
actualType: (descriptor: SerialDescriptor) -> String = { it.kind.toString() } | ||
|
@@ -87,6 +151,10 @@ internal open class DefaultBsonDecoder( | |
} | ||
} | ||
|
||
private data class ElementMetadata(val name: String, val nullable: Boolean, var processed: Boolean = false) | ||
private var elementsMetadata: Array<ElementMetadata>? = null | ||
private var currentIndex: Int = UNKNOWN_INDEX | ||
|
||
private fun initElementMetadata(descriptor: SerialDescriptor) { | ||
if (this.elementsMetadata != null) return | ||
val elementsMetadata = | ||
|
@@ -134,14 +202,13 @@ internal open class DefaultBsonDecoder( | |
?: UNKNOWN_NAME | ||
} | ||
|
||
@Suppress("ReturnCount") | ||
override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder { | ||
return when (descriptor.kind) { | ||
is StructureKind.LIST -> BsonArrayDecoder(descriptor, reader, serializersModule, configuration) | ||
is PolymorphicKind -> PolymorphicDecoder(descriptor, reader, serializersModule, configuration) | ||
is PolymorphicKind -> createBsonPolymorphicDecoder(descriptor, reader, serializersModule, configuration) | ||
is StructureKind.LIST -> createBsonArrayDecoder(descriptor, reader, serializersModule, configuration) | ||
is StructureKind.CLASS, | ||
StructureKind.OBJECT -> BsonDocumentDecoder(descriptor, reader, serializersModule, configuration) | ||
is StructureKind.MAP -> MapDecoder(descriptor, reader, serializersModule, configuration) | ||
StructureKind.OBJECT -> createBsonDocumentDecoder(descriptor, reader, serializersModule, configuration) | ||
is StructureKind.MAP -> createBsonMapDecoder(descriptor, reader, serializersModule, configuration) | ||
else -> throw SerializationException("Primitives are not supported at top-level") | ||
} | ||
} | ||
|
@@ -152,18 +219,15 @@ internal open class DefaultBsonDecoder( | |
is StructureKind.MAP, | ||
StructureKind.CLASS, | ||
StructureKind.OBJECT -> reader.readEndDocument() | ||
else -> super.endStructure(descriptor) | ||
else -> {} | ||
} | ||
} | ||
|
||
override fun decodeByte(): Byte = NumberCodecHelper.decodeByte(reader) | ||
|
||
override fun decodeChar(): Char = StringCodecHelper.decodeChar(reader) | ||
override fun decodeFloat(): Float = NumberCodecHelper.decodeFloat(reader) | ||
|
||
override fun decodeShort(): Short = NumberCodecHelper.decodeShort(reader) | ||
override fun decodeBoolean(): Boolean = reader.readBoolean() | ||
|
||
override fun decodeDouble(): Double = NumberCodecHelper.decodeDouble(reader) | ||
override fun decodeInt(): Int = NumberCodecHelper.decodeInt(reader) | ||
override fun decodeLong(): Long = NumberCodecHelper.decodeLong(reader) | ||
|
@@ -183,7 +247,6 @@ 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 reader(): BsonReader = reader | ||
|
||
private inline fun <T> readOrThrow(action: () -> T, bsonType: BsonType): T { | ||
return try { | ||
|
@@ -197,13 +260,20 @@ internal open class DefaultBsonDecoder( | |
} | ||
} | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
private class BsonArrayDecoder( | ||
/** The default Bson Decoder implementation */ | ||
internal open class BsonDecoderImpl( | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
) : AbstractBsonDecoder(reader, serializersModule, configuration) | ||
|
||
/** The Bson array decoder */ | ||
internal open class BsonArrayDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
) : DefaultBsonDecoder(reader, serializersModule, configuration) { | ||
) : AbstractBsonDecoder(reader, serializersModule, configuration) { | ||
|
||
init { | ||
validateCurrentBsonType(reader, BsonType.ARRAY, descriptor) | ||
|
@@ -218,13 +288,29 @@ private class BsonArrayDecoder( | |
} | ||
} | ||
|
||
/** The Bson document decoder */ | ||
@OptIn(ExperimentalSerializationApi::class) | ||
private class PolymorphicDecoder( | ||
internal open class BsonDocumentDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
) : DefaultBsonDecoder(reader, serializersModule, configuration) { | ||
) : AbstractBsonDecoder(reader, serializersModule, configuration) { | ||
|
||
init { | ||
validateCurrentBsonType(reader, BsonType.DOCUMENT, descriptor) { it.serialName } | ||
reader.readStartDocument() | ||
} | ||
} | ||
|
||
/** The Bson polymorphic class decoder */ | ||
@OptIn(ExperimentalSerializationApi::class) | ||
internal open class BsonPolymorphicDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
) : AbstractBsonDecoder(reader, serializersModule, configuration) { | ||
private var index = 0 | ||
private var mark: BsonReaderMark? | ||
|
||
|
@@ -239,7 +325,7 @@ private class PolymorphicDecoder( | |
it.reset() | ||
mark = null | ||
} | ||
return deserializer.deserialize(DefaultBsonDecoder(reader, serializersModule, configuration)) | ||
return deserializer.deserialize(BsonDecoder.createBsonDecoder(reader, serializersModule, configuration)) | ||
} | ||
|
||
override fun decodeElementIndex(descriptor: SerialDescriptor): Int { | ||
|
@@ -266,27 +352,14 @@ private class PolymorphicDecoder( | |
} | ||
} | ||
|
||
/** The Bson map decoder */ | ||
@OptIn(ExperimentalSerializationApi::class) | ||
private class BsonDocumentDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
) : DefaultBsonDecoder(reader, serializersModule, configuration) { | ||
init { | ||
validateCurrentBsonType(reader, BsonType.DOCUMENT, descriptor) { it.serialName } | ||
reader.readStartDocument() | ||
} | ||
} | ||
|
||
@OptIn(ExperimentalSerializationApi::class) | ||
private class MapDecoder( | ||
internal open class BsonMapDecoder( | ||
descriptor: SerialDescriptor, | ||
reader: AbstractBsonReader, | ||
serializersModule: SerializersModule, | ||
configuration: BsonConfiguration | ||
) : DefaultBsonDecoder(reader, serializersModule, configuration) { | ||
|
||
) : AbstractBsonDecoder(reader, serializersModule, configuration) { | ||
private var index = 0 | ||
private var isKey = false | ||
|
||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.