Is it possible to create a serializer for any List...
# serialization
a
Is it possible to create a serializer for any List of @Serializable ? I need in a lot of place a list of element where if there is only one element, then the element is inlined. Know that it's only for JsonSerialization and I don't need deserialization. For example if I have
elements = listOf("test")
it will only be serialized as
"elements": "test"
but if I have
elements = listOf("foo", "bar")
then it will be serialized as
"elements": ["foo", "bar"]
j
Copy code
sealed interface Foo

data class Bar(val elements: String) : Foo
data class Baz(val elements: List<String>) : Foo
a
I see three ways of doing this 1. sealed type explicit types for singles/list (as @Javier suggested), with a JsonContentPolymorphicSerializer 2. a value class with a list value, with a custom KSerializer 3. a typealias serializer, with a custom KSerializer Option 1 would be best if you need to know whether the JSON had a single or multiple elements, but I don’t think this sounds like your situation. It doesn’t really matter how the JSON looks, right? Option 2 is nice because you’d have an explicit type, so you could be certain about which types are being encoded correctly or not. However, value classes can be a bit annoying to convert to/from, which would probably be necessary even if you used interface-delegation. Option 3 is probably best for your situation, because it’s completely transparent and you don’t really have to think about the weird single-element-list-inlining behaviour in your code. However, you’d have to be careful to make sure that you always used your typealias - it would be easy to forget.
I’d probably go for option 3, since it’s the most frictionless to use in the code, and create a typealias and use it in all of your
@Serializable
classes
Copy code
typealias InlinableList<T> = @Serializable(with = InlinableListSerializer::class) List<T>

@Serializable
data class MyJsonData(
  val strings: InlinableList<String>,
  val integers: InlinableList<Int>,
)
Then you just need to create the InlinableListSerializer:
Copy code
class InlinableListSerializer<T>(
  elementSerializer: KSerializer<T>
) : KSerializer<List<T>> {
  // ...
}
Because InlinableList has a generic type the constructor for InlinableListSerializer must have a single parameter for the serializer for
T
, which you can use in the
serialize()
/
deserialize()
functions. You’ve mentioned in your other messages that you’re only interested in deserializing, so I’ll just create a quick demo for that:
Copy code
override fun deserialize(decoder: Decoder): List<T> {
    require(decoder is JsonDecoder) { "only JSON is supported" }
    val element = decoder.decodeJsonElement()
    return when (element) {
      is JsonPrimitive -> {
        val single = TODO("deserialize a single element using elementSerializer")
        listOf(single)
      }
      is JsonArray -> {
        TODO("deserialize a using ListSerializer(elementSerializer)")
      }
      else -> error("unsupported JSON element $element")
    }
  )
a
Thanks for your very detailed reply, yes Option 3 is exactly what I need, and doing a serializer for the List class as a typealias is a pretty interesting solution. You should just read again my messages as I said that I'm not interested in deserialization, but no problem, I'll create the serialize function myself !
a
ahh sorry, I mixed up serializer/deserialize :)
a
No problem 🙂