Kotlinx.serialization: Serializing a ClosedFloatingPointRange<Double> failed

Created on 12 Sep 2020  路  1Comment  路  Source: Kotlin/kotlinx.serialization

When trying to serialize an CloseFloatingPointRange\<Double>, Serialization complained:

Exception in thread "main" kotlinx.serialization.SerializationException: Class 'ClosedDoubleRange' is not registered for polymorphic serialization in the scope of 'ClosedFloatingPointRange'.
Mark the base class as 'sealed' or register the serializer explicitly.
    at kotlinx.serialization.internal.AbstractPolymorphicSerializerKt.throwSubtypeNotRegistered(AbstractPolymorphicSerializer.kt:103)
    at kotlinx.serialization.internal.AbstractPolymorphicSerializerKt.throwSubtypeNotRegistered(AbstractPolymorphicSerializer.kt:113)
    at kotlinx.serialization.PolymorphicSerializerKt.findPolymorphicSerializer(PolymorphicSerializer.kt:96)
    at kotlinx.serialization.json.internal.PolymorphicKt.findActualSerializer(Polymorphic.kt:29)
    at kotlinx.serialization.json.internal.PolymorphicKt.access$findActualSerializer(Polymorphic.kt:1)
    at kotlinx.serialization.json.internal.StreamingJsonEncoder.encodeSerializableValue(StreamingJsonEncoder.kt:226)
    at kotlinx.serialization.encoding.AbstractEncoder.encodeSerializableElement(AbstractEncoder.kt:84)
    at SomeClass.write$Self(AnySerialization.kt)
    at SomeClass$$serializer.serialize(AnySerialization.kt)
    at SomeClass$$serializer.serialize(AnySerialization.kt:6)
    at kotlinx.serialization.json.internal.StreamingJsonEncoder.encodeSerializableValue(StreamingJsonEncoder.kt:223)
    at kotlinx.serialization.json.Json.encodeToString(Json.kt:73)
    at AnySerializationKt.main(AnySerialization.kt:16)
    at AnySerializationKt.main(AnySerialization.kt)

and ClosedDoubleRange is private in Kotlin stdlib. Thus, if I understood correctly, it is impossible to write a custom @Serializer for such classes?

To reproduce this:

```kotlin
@Serializable
data class SomeClass(
val range: ClosedFloatingPointRange
)

val x = Json.encodeToString(SomeClass(0.0.rangeTo(1.0)))

````

Environment

  • Kotlin version: 1.4.1
  • Library version: 1.0.0-RC
  • Kotlin platforms: JVM
question

Most helpful comment

It is completely possible.

E.g. take a look at this serializer:

object CFPRSerializer : KSerializer<ClosedFloatingPointRange<Double>> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("kotlin.ranges.DoubleClosedFloatingPointRange") {
        element<Double>("start")
        element<Double>("endEnclusive")
    }

    override fun serialize(encoder: Encoder, value: ClosedFloatingPointRange<Double>) {
        encoder.encodeStructure(descriptor) {
            encodeDoubleElement(descriptor, 0, value.start)
            encodeDoubleElement(descriptor, 1, value.endInclusive)
        }
    }

    override fun deserialize(decoder: Decoder): ClosedFloatingPointRange<Double> {
        decoder.decodeStructure(descriptor) {
            var start: Double? = null
            var end: Double? = null
            while (true) {
                val index = decodeElementIndex(descriptor)
                if (index == CompositeDecoder.DECODE_DONE) break
                if (index == 0) start = decodeDoubleElement(descriptor, index)
                else end = decodeDoubleElement(descriptor, index)
            }
            if (start == null || end == null) throw SerializationException("...")
            return start..end
        }
    }
}

@Serializable
data class SomeClass(
    @Serializable(with = CFPRSerializer::class) val range: ClosedFloatingPointRange<Double>
)

>All comments

It is completely possible.

E.g. take a look at this serializer:

object CFPRSerializer : KSerializer<ClosedFloatingPointRange<Double>> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("kotlin.ranges.DoubleClosedFloatingPointRange") {
        element<Double>("start")
        element<Double>("endEnclusive")
    }

    override fun serialize(encoder: Encoder, value: ClosedFloatingPointRange<Double>) {
        encoder.encodeStructure(descriptor) {
            encodeDoubleElement(descriptor, 0, value.start)
            encodeDoubleElement(descriptor, 1, value.endInclusive)
        }
    }

    override fun deserialize(decoder: Decoder): ClosedFloatingPointRange<Double> {
        decoder.decodeStructure(descriptor) {
            var start: Double? = null
            var end: Double? = null
            while (true) {
                val index = decodeElementIndex(descriptor)
                if (index == CompositeDecoder.DECODE_DONE) break
                if (index == 0) start = decodeDoubleElement(descriptor, index)
                else end = decodeDoubleElement(descriptor, index)
            }
            if (start == null || end == null) throw SerializationException("...")
            return start..end
        }
    }
}

@Serializable
data class SomeClass(
    @Serializable(with = CFPRSerializer::class) val range: ClosedFloatingPointRange<Double>
)
Was this page helpful?
0 / 5 - 0 ratings