Reputation: 3395
I have a data class which has a property whose type is another data class, like this:
@Serializable
data class Vehicle (
val color: String,
val miles: Int,
val year: Int,
val garage: Garage
)
@Serializable
data class Garage (
val latitude: Float,
val longitude: Float,
val name: String
)
Upon serializing, it produces output like this:
{
"color" : "black" ,
"miles" : 35000 ,
"year" : 2017 ,
"garage" : { "latitude" : 43.478342 , "longitude" : -91.337000 , "name" : "Paul's Garage" }
}
However I would like garage
to be a literal string of its JSON representation, not an actual JSON object. In other words, the desired output is:
{
"color" : "black" ,
"miles" : 35000 ,
"year" : 2017 ,
"garage" : "{ \"latitude\" : 43.478342 , \"longitude\" : -91.337000 , \"name\" : \"Paul's Garage\" }"
}
How can I accomplish this in Kotlin? Can it be done with just kotlinx.serialization
or is Jackson/Gson absolutely necessary?
Note that this output is for a specific usage. I cannot overwrite the base serializer because I still need to serialize/deserialize from normal JSON (the first example). In other words, the best scenario would be to convert the first JSON sample to the second, not necessarily to have the data class produce the 2nd sample directly.
Thanks!
Upvotes: 2
Views: 2462
Reputation: 500
Here is a solution with a custom serializer for Garage
and an additional class for Vehicle
.
Garage
to String
serializer:
object GarageToStringSerializer : KSerializer<Garage> {
override val descriptor = PrimitiveSerialDescriptor("GarageToString", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Garage) = encoder.encodeString(Json.encodeToString(value))
override fun deserialize(decoder: Decoder): Garage = Json.decodeFromString(decoder.decodeString())
}
Auxiliary class:
@Serializable
data class VehicleDto(
val color: String,
val miles: Int,
val year: Int,
@Serializable(GarageToStringSerializer::class)
val garage: Garage
) {
constructor(v: Vehicle) : this(v.color, v.miles, v.year, v.garage)
}
The demanded result can be received with:
Json.encodeToString(VehicleDto(vehicle))
Upvotes: 0
Reputation: 4795
Create a custom SerializationStrategy
for Vehicle
as follows:
val vehicleStrategy = object : SerializationStrategy<Vehicle> {
override val descriptor: SerialDescriptor
get() = buildClassSerialDescriptor("Vehicle") {
element<String>("color")
element<Int>("miles")
element<Int>("year")
element<String>("garage")
}
override fun serialize(encoder: Encoder, value: Vehicle) {
encoder.encodeStructure(descriptor) {
encodeStringElement(descriptor, 0, value.color)
encodeIntElement(descriptor, 1, value.miles)
encodeIntElement(descriptor, 2, value.year)
encodeStringElement(descriptor, 3, Json.encodeToString(value.garage))
}
}
}
Then pass it to Json.encodeToString()
:
val string = Json.encodeToString(vehicleStrategy, vehicle)
Result:
{"color":"black","miles":35000,"year":2017,"garage":"{\"latitude\":43.47834,\"longitude\":-91.337,\"name\":\"Paul's Garage\"}"}
More info here
Upvotes: 1