How do I make a CustomSerializer for a Map<Stri...
# serialization
d
How do I make a CustomSerializer for a Map<String, Any> that
Any
can either contain a
String
a
Set<String>
or a
Map<String, Set<String>>
?
a
I'd make a sealed interface to represent the possible values. Then make 3
@Serializable
value class implementations for the String, SetString, and Map<> subtypes.
Copy code
@Serializable
sealed interface CustomValue

@Serializable
value class StringValue(val value: String): CustomValue, CharSequence by value

@Serializable
value class StringSetValue(val value: Set<String>): CustomValue, Set<String> by value

@Serializable
value class MapStringToSetStringValue(val value: Map<String, Set<String>>): CustomValue, Map<String, Set<String>> by value
Then instead of
Map<String, Any>
, use
Map<String, CustomValue>
d
That's a very nice way! But the use value classes there are probably boxed (but better than just using JsonObject...), and doesn't that add an extra type field in the json?
a
Yeah exactly, the value classes are really useful for KxS because they're 'transparent', so KxS will encode StringValue as a string, not a JSON object. But the value classes might be boxed.
👍🏼 1
And yes, it will add a type field. If you want to avoid that, you'd have to create a custom serializer.
d
I'm not sure where to start... a custom descriptor?
a
actually, thinking about it, I'm not sure what will happen with the type field, given that the subclasses aren't objects 🤔
custom serializers can be tricky, but for JSON they're pretty easy. Start with the JsonContentPolymorphicSerializer docs
d
That looks interesting, but it seems to generate a JsonElement... is that what's done anyways by generated serializers? It's probably better than converting everything to one big JsonObject, since it (hopefully) doesn't leave all that in memory... but still... wondering if using a regular customserializer might be more efficient?
Thanks a lot for the help though! I'll try the JsonContentPolymorphicSerializer..
Just a little detail... I have this:
Copy code
object RestrictionSerializer : JsonContentPolymorphicSerializer<Any>(Any::class) {
    override fun selectDeserializer(element: JsonElement): DeserializationStrategy<Any> {
        return when (element) {
            is JsonPrimitive -> String.serializer()
            is JsonArray -> ListSerializer(String.serializer())
            is JsonObject -> MapSerializer(String.serializer(), ListSerializer(String.serializer()))
        }
    }
}
but how to I use it to serialize/deserialize this:
Copy code
data class Foo(private val restrictions: Map<String, Any>) // where the Any is supposed to use that serializer?
@Adam S
Never mind, I think I got it. I just need to annotate the Any with
@Serializable(with = RestrictionsSerializer::class)
...
Btw, thanks a lot for the sealed interface idea, it really helps making my code clearer than with that Any I had before!
a
Nice! Yeah, it's really confusing using Any. But if you want a KxS equivalent, you can also use JsonElement, and then manually convert/reject the JsonElement later on in the code. It's an alternative to writing a custom serializer (which can be quite tricky).
wondering if using a regular customserializer might be more efficient?
I'm pretty sure that KxS will dynamically convert the content to JsonElements on demand, in a performance friendly way. But I'm not 100% sure. But it's usually only worth worrying about performance if you've written performance tests :)
👍🏼 1
154 Views