Пользовательский сериализатор с полиморфной сериализацией kotlinx
С полиморфизмом kotlinx.serialization я хочу получить
{"type":"veh_t","owner":"Ivan","bodyType":"cistern","carryingCapacityInTons":5,"detachable":false}
но я получаю
{"type":"kotlin.collections.LinkedHashMap","owner":"Ivan","bodyType":"cistern","carryingCapacityInTons":5,"detachable":false}
Использую следующие модели
interface Vehicle {
val owner: String
}
@Serializable
@SerialName("veh_p")
data class PassengerCar(
override val owner: String,
val numberOfSeats: Int
) : Vehicle
@Serializable
@SerialName("veh_t")
data class Truck(
override val owner: String,
val body: Body
) : Vehicle {
@Serializable
data class Body(
val bodyType: String,
val carryingCapacityInTons: Int,
val detachable: Boolean
//a lot of other fields
)
}
Применяю следующий Json
inline val VehicleJson: Json get() = Json(context = SerializersModule {
polymorphic(Vehicle::class) {
PassengerCar::class with PassengerCar.serializer()
Truck::class with TruckKSerializer
}
})
Я использую сериализатор TruckKSerializer, потому что сервер имеет плоскую структуру. В то же время в приложении я хочу использовать объект Truck.Body. Для сглаживания я отменяюfun serialize(encoder: Encoder, obj : T)
а также fun deserialize(decoder: Decoder): T
в Сериализаторе с использованием JsonOutput и JsonInput согласно документации этих классов.
object TruckKSerializer : KSerializer<Truck> {
override val descriptor: SerialDescriptor = SerialClassDescImpl("Truck")
override fun serialize(encoder: Encoder, obj: Truck) {
val output = encoder as? JsonOutput ?: throw SerializationException("This class can be saved only by Json")
output.encodeJson(json {
obj::owner.name to obj.owner
encoder.json.toJson(Truck.Body.serializer(), obj.body)
.jsonObject.content
.forEach { (name, value) ->
name to value
}
})
}
@ImplicitReflectionSerializer
override fun deserialize(decoder: Decoder): Truck {
val input = decoder as? JsonInput
?: throw SerializationException("This class can be loaded only by Json")
val tree = input.decodeJson() as? JsonObject
?: throw SerializationException("Expected JsonObject")
return Truck(
tree.getPrimitive("owner").content,
VehicleJson.fromJson<Truck.Body>(tree)
)
}
}
И наконец, я использую stringify(serializer: SerializationStrategy<T>, obj: T)
VehicleJson.stringify(
PolymorphicSerializer(Vehicle::class),
Truck(
owner = "Ivan",
body = Truck.Body(
bodyType = "cistern",
carryingCapacityInTons = 5,
detachable = false
)
)
)
Я заканчиваю с {"type":"kotlin.collections.LinkedHashMap", ...}
, но мне нужно {"type":"veh_t", ...}
Как мне получить правильный тип? Я хочу использовать полиморфизм дляVehicle
и закодировать объект Body с помощью Truck.Body.serializer() для сглаживания.
С этой сериализацией класс PassengerCar работает нормально.
VehicleJson.stringify(
PolymorphicSerializer(Vehicle::class),
PassengerCar(
owner = "Oleg",
numberOfSeats = 4
)
)
Результат правильный:
{"type":"veh_p","owner":"Oleg","numberOfSeats":4}
Думаю, проблема в кастомном сериализаторе TruckKSerializer
. И я заметил, использую ли я в своем переопределенномfun serialize(encoder: Encoder, obj : T)
следующий код
encoder
.beginStructure(descriptor)
.apply {
//...
}
.endStructure(descriptor)
Я получил правильный тип, но не могу сгладить объект Truck.Body с помощью его сериализатора.
1 ответ
Правильный способ открытия и закрытия композита {}
это код
val composite = encoder.beginStructure(descriptor)
// use composite instead of encoder here
composite.endStructure(descriptor)
и вы сможете сериализовать Body
с помощью .encodeSerializable(Body.serializer(), body)
и всегда передавайте дескриптор, иначе он вернется к вещам вроде LinkedhashMap для словаря json