commonMain.internal.PolymorphicSerializerNbtAdapter.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of knbt Show documentation
Show all versions of knbt Show documentation
Minecraft NBT support for kotlinx.serialization
package net.benwoodworth.knbt.internal
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.PolymorphicKind
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.*
import kotlinx.serialization.internal.AbstractPolymorphicSerializer
import kotlinx.serialization.modules.SerializersModule
import net.benwoodworth.knbt.*
@OptIn(ExperimentalSerializationApi::class, InternalSerializationApi::class)
internal class PolymorphicSerializerNbtAdapter(
private val polymorphicSerializer: AbstractPolymorphicSerializer
) : KSerializer {
override val descriptor: SerialDescriptor
get() = polymorphicSerializer.descriptor
override fun serialize(encoder: Encoder, value: T) {
val nbtEncoder = encoder.asNbtEncoder()
val classDiscriminator = nbtEncoder.nbt.configuration.classDiscriminator
val polymorphicEncoder = PolymorphicEncoder(nbtEncoder)
polymorphicSerializer.serialize(polymorphicEncoder, value)
val encodedType = checkNotNull(polymorphicEncoder.encodedType) {
"Expected polymorphic 'type' element to be encoded by ${polymorphicSerializer::class.simpleName}"
}
val encodedValue = checkNotNull(polymorphicEncoder.encodedValue) {
"Expected polymorphic 'value' element to be encoded by ${polymorphicSerializer::class.simpleName}"
}
val encodedValueDescriptor = checkNotNull(polymorphicEncoder.encodedValueDescriptor) {
"Encoded value descriptor should have been recorded alongside the encoded value"
}
if (encodedValue !is NbtCompound) {
throw NbtDecodingException("Expected polymorphic data to be a ${NbtTagType.TAG_Compound}, but was ${encodedValue.type}")
}
if (classDiscriminator in encodedValue) {
val baseName = polymorphicSerializer.descriptor.serialName
val actualName = encodedValueDescriptor.serialName
val isSealed = polymorphicSerializer.descriptor.kind == PolymorphicKind.SEALED
val classType = if (isSealed) "Sealed" else "Polymorphic"
error(
"$classType class '$actualName' cannot be serialized as base class '$baseName' because it " +
"has property name that conflicts with NBT class discriminator '$classDiscriminator'. " +
"Consider renaming property with @SerialName annotation"
)
}
val encodedValueWithDiscriminator = buildNbtCompound {
put(classDiscriminator, encodedType)
encodedValue.forEach { (key, value) ->
put(key, value)
}
}
nbtEncoder.encodeSerializableValue(NbtCompoundSerializer, encodedValueWithDiscriminator)
}
override fun deserialize(decoder: Decoder): T {
val nbtDecoder = decoder.asNbtDecoder()
val classDiscriminator = nbtDecoder.nbt.configuration.classDiscriminator
val decodedValueWithDiscriminator = nbtDecoder.decodeSerializableValue(NbtTag.serializer())
if (decodedValueWithDiscriminator !is NbtCompound) {
throw NbtDecodingException("Expected polymorphic data to be a ${NbtTagType.TAG_Compound} with a '$classDiscriminator' class discriminator, but was ${decodedValueWithDiscriminator.type}")
}
val decodedType = decodedValueWithDiscriminator[classDiscriminator]
?: throw NbtDecodingException("Expected polymorphic data to be a ${NbtTagType.TAG_Compound} with a '$classDiscriminator' class discriminator, but was ${decodedValueWithDiscriminator.type}")
if (decodedType !is NbtString) {
throw NbtDecodingException("Expected polymorphic 'type' class discriminator to be a ${NbtTagType.TAG_String}, but was ${decodedType.type}")
}
val decodedValue = buildNbtCompound {
decodedValueWithDiscriminator.forEach { (key, value) ->
if (key != classDiscriminator) put(key, value)
}
}
return polymorphicSerializer.deserialize(PolymorphicDecoder(nbtDecoder, decodedType.value, decodedValue))
}
private class PolymorphicEncoder(
private val nbtEncoder: NbtEncoder
) : AbstractEncoder() {
override val serializersModule: SerializersModule
get() = nbtEncoder.serializersModule
var encodedType: String? = null
var encodedValue: NbtTag? = null
var encodedValueDescriptor: SerialDescriptor? = null
override fun encodeString(value: String) {
encodedType = value
}
override fun encodeSerializableValue(serializer: SerializationStrategy, value: T) {
val writer = TreeNbtWriter {
encodedValue = it
encodedValueDescriptor = serializer.descriptor
}
DefaultNbtEncoder(nbtEncoder.nbt, writer)
.encodeSerializableValue(serializer, value)
}
}
private class PolymorphicDecoder(
private val nbtDecoder: NbtDecoder,
private val decodedType: String,
private val decodedValue: NbtCompound
) : AbstractDecoder() {
override val serializersModule: SerializersModule
get() = nbtDecoder.serializersModule
private var nextElementIndex = 0
override fun decodeElementIndex(descriptor: SerialDescriptor): Int =
if (nextElementIndex <= 1) {
nextElementIndex++
} else {
CompositeDecoder.DECODE_DONE
}
override fun decodeString(): String = decodedType
override fun decodeSerializableValue(deserializer: DeserializationStrategy): T =
NbtDecoder(nbtDecoder.nbt, TreeNbtReader(decodedValue))
.decodeSerializableValue(deserializer)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy