简体   繁体   中英

How to ignore unknown types with kotlinx.serialization?

I have several classes that I want to deserialize, that include lists of polymorphic types. I can get it to deserialize correctly known types, but deserializing an unknown type throws an exception. What I really want is that the list includes only known types and unknown types are just filtered out. If this could be done in a generic way would be even better.

sealed interface BaseInterface

interface I1 : BaseInterface {
  fun f1(): String
}

interface I2: BaseInterface {
  fun f2(): String
}

@Serializable
@SerialName("i1")
data class I1Rest(value: String): I1 {
  fun f1(): String = value
}

@Serializable
@SerialName("i2")
data class I2Rest(value: String): I2 {
  fun f2(): String = value
}

@Serializable
data class SomeClass(list: List<BaseInterface>)

How can I can correctly deserialize SomeClass with

{ "list": [ {"type": "i1", "value": "v1" }, {"type": "i2", "value": "v2" }, {"type": "i3", "value": "v3" } ] }

If I don't add the i3 type to the json, I can correctly deserialize it using

SerializersModule {
  polymorphic(BaseInterface::class){
    subclass(I1Rest::class)
    subclass(I2Rest::class)
  }
}

But as soon as I include an unknown type, it breaks. Note that I don't want to deserialize unknowns to a default type (that would have to extend the base sealed interface). What I want is to ignore/filter the unknowns. (preferably in a generic way) I would also would like to keep the BaseInterface as an interface and not a class, because I only want to expose interfaces and not concrete classes (this is for a lib)

Ok, this is the best I could come up with:

@Serializable
data class SomeClass(
    @Serializable(with = UnknownBaseInterfaceTypeFilteringListSerializer::class)
    val list: List<BaseInterface>
)

val json = Json {
    ignoreUnknownKeys = true
    serializersModule = SerializersModule {
        polymorphic(BaseInterface::class) {
            subclass(I1Rest::class)
            subclass(I2Rest::class)
            defaultDeserializer { UnknownTypeSerializer() }
        }
    }
}

class FilteringListSerializer<E>(private val elementSerializer: KSerializer<E>) : KSerializer<List<E>> {
    private val listSerializer = ListSerializer(elementSerializer)
    override val descriptor: SerialDescriptor = listSerializer.descriptor

    override fun serialize(encoder: Encoder, value: List<E>) {
        listSerializer.serialize(encoder, value)
    }

    override fun deserialize(decoder: Decoder): List<E> = with(decoder as JsonDecoder) {
        decodeJsonElement().jsonArray.mapNotNull {
            try {
                json.decodeFromJsonElement(elementSerializer, it)
            } catch (e: UnknownTypeException) {
                null
            }
        }
    }
}

class UnknownTypeException(message: String) : SerializationException(message)

open class UnknownTypeSerializer<T> : KSerializer<T> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Nothing")
    override fun deserialize(decoder: Decoder): T = throw UnknownTypeException("unknown type")
    override fun serialize(encoder: Encoder, value: T) = throw UnknownTypeException("unknown type")
}

object UnknownBaseInterfaceTypeFilteringListSerializer : KSerializer<List<BaseInterface>> by FilteringListSerializer(PolymorphicSerializer(BaseInterface::class))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM