0

Let's assume I have following json objects :

{
    "type": "video",
    "...": "..."
}
{
    "type": "image",
    "...": "..."
}

They both represent media object. Kotlin sealed model looks like :

sealed class Media {
    ...
}

@Serializable
@SerialName("video")
data class Video(...) : Media()

@Serializable
@SerialName("image")
data class Image(...) : Media()

According KoltinX doc, I used a wrapper for polymorphic serialization :

@Serializable
private data class MediaWrapper(@Polymorphic val media: Media) {
    companion object {
        val jsonSerializer = Json(
            context = SerializersModule {
                polymorphic<Media> {
                    Video::class with Video.serializer()
                    Image::class with Image.serializer()
                }
            }
        )

        fun fromJson(json: String) = jsonSerializer.parse(serializer(), json)
    }
}

The goal is to deserialize a Media json using my wrapper, but problem is I need to change my Media json into a MediaWrapper json. The most convenient solution I found is to add {\"media\":\" & \"} on each side of my Media json:

sealed class Media {
    companion object {
        fun fromJson(mediaJson: String): Media {
            val mediaWrapperJson = "{\"media\":$mediaJson}"
            val mediaWrapper = MediaWrapper.fromJson(mediaWrapperJson)
            return mediaWrapper.media
        }
    }
}

This is a trick, if there is a more convenient way to deserialize polymorphics, please let me know!

Nicolas Duponchel
  • 1,219
  • 10
  • 17
  • how I should apply this to my dataclass containing the field? With @SerializableName then write custom deserializer? Please extend – nutella_eater Nov 19 '19 at 11:32

1 Answers1

3

While the kotlinx serialization docs use a wrapper in many of its polymorphic examples, it does not say that this pattern is mandatory.

From the docs:

Pro tip: to use Message without a wrapper, you can pass PolymorphicSerializer(Message::class) to parse/stringify.

In your case you could do:

sealed class Media {
    companion object {
        val jsonSerializer = Json(
            context = SerializersModule {
                polymorphic<Media> {
                    Video::class with Video.serializer()
                    Image::class with Image.serializer()
                }
            }
        )

        fun fromJson(mediaJson: String): Media {
           return jsonSerializer.parse(PolymorphicSerializer(Media::class), mediaJson) as Media
        }
    }
}
user10350526
  • 176
  • 5
  • Working great thanks! Juste a detail : `Type inference failed. Expected type mismatch. Requiered: Media. Found: Any` I had to put `as Media` cast after `parse` – Nicolas Duponchel Sep 09 '19 at 08:17
  • For me, this wasn't working until I realized that kotlinx needs a `"type"` attribute within the JSON to differentiate between classes. So, if you decorate `Video` with `@SerialName("video")`, the input JSON string should look like `{ "type": "video", ... }`. – Mateen Ulhaq Nov 17 '19 at 12:33
  • how I should apply this to my dataclass containing the field? With @SerializableName then write custom deserializer? Please extend – nutella_eater Nov 19 '19 at 11:32