Пользовательский сериализатор с полиморфной сериализацией kotlinx - PullRequest
0 голосов
/ 18 октября 2019

С полиморфизмом 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 в Serializator, используя 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 Ответ

0 голосов
/ 21 октября 2019

правильный способ открывать и закрывать композит {} - это код

val composite = encoder.beginStructure(descriptor)
// use composite instead of encoder here
composite.endStructure(descriptor)

, и вы должны иметь возможность сериализовать Body, используя .encodeSerializable(Body.serializer(), body)

и всегда проходитьдескриптор вместе, иначе он обратится к тому же LinkedhashMap для словаря json

...