Path is an interface, so it's implicitly serializable with the PolymorphicSerializer strategy. This strategy requires you to register serializers for subclasses implementing it, but as you know, in this case it's impossible.
There is a default polymorphic serializer, but it affects only deserialization process and it works only when deserializable value is a JSONObject.
And for the following serializer
object PathAsStringSerializer : KSerializer<Path> {
override val descriptor = PrimitiveSerialDescriptor("Path", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Path) = encoder.encodeString(value.toAbsolutePath().toString())
override fun deserialize(decoder: Decoder): Path = Path.of(decoder.decodeString())
}
\\Not working
val module = SerializersModule { polymorphicDefault(Path::class) { PathAsStringSerializer } }
val decoded : Path = Json { serializersModule = module }.decodeFromString("C:\\Temp")
it will throw runtime exception kotlinx.serialization.json.internal.JsonDecodingException: Expected class kotlinx.serialization.json.JsonObject as the serialized body of kotlinx.serialization.Polymorphic<Path>, but had class kotlinx.serialization.json.JsonLiteral
So, it can't be serialized in a common way, and there are 3 cases of its serialization/deserialization, which need to be handled:
1. Serialization of simple Path variable
In this case you need to explicitly pass your custom serializer:
val path = Path.of("C:\\Temp")
val message1 = Json.encodeToString(PathAsStringSerializer, path).also { println(it) }
println(Json.decodeFromString(PathAsStringSerializer, message1))
2. Serialization of classes, which use Path as a generic parameter
In this case you need to define separate serializers (you may reference original PathAsStringSerializer) and also explicitly pass them:
object ListOfPathsAsStringSerializer : KSerializer<List<Path>> by ListSerializer(PathAsStringSerializer)
val message2 = Json.encodeToString(ListOfPathsAsStringSerializer, listOf(path)).also { println(it) }
println(Json.decodeFromString(ListOfPathsAsStringSerializer, message2))
@Serializable
data class Box<T>(val item: T)
object BoxOfPathSerializer : KSerializer<Box<Path>> by Box.serializer(PathAsStringSerializer)
val message3 = Json.encodeToString(BoxOfPathSerializer, Box(path)).also { println(it) }
println(Json.decodeFromString(BoxOfPathSerializer, message3))
3. Serialization of classes, which have fields of aforementioned types
In this case you need to add respectful @Serializable(with = ...) annotations for these fields:
@Serializable
data class InnerObject(
@Serializable(with = ListOfPathsAsStringSerializer::class)
val list: MutableList<Path> = mutableListOf(),
@Serializable(with = PathAsStringSerializer::class)
val path: Path,
@Serializable(with = BoxOfPathSerializer::class)
val box: Box<Path>
)
Or just list them once for a whole file:
@file: UseSerializers(PathAsStringSerializer::class, ListOfPathsAsStringSerializer::class, BoxOfPathSerializer::class)
Plugin-generated serializer for this case would be good enough:
val message4 = Json.encodeToString(InnerObject(mutableListOf(path), path, Box(path))).also { println(it) }
println(Json.decodeFromString<InnerObject>(message4))