My system persists the following object graph as J...
# serialization
i
My system persists the following object graph as JSON serialized with kotlinx.serialization:
Copy code
@Serializable
data class Category(val id: String, val children: Set<Category> = emptySet())
For the sake of having “stable” diff of the updated JSON file, I’d like to enforce order of the
children
Set’s elements based on lexicographic order of the element’s
id
field. The solution I was able to come up with is to use custom serializer based on `JsonTransformingSerializer`:
Copy code
object CategorySetSerializer : JsonTransformingSerializer<Set<Category>>(SetSerializer(Category.serializer())) {
    override fun transformSerialize(element: JsonElement) = super.transformSerialize(
        if (element is JsonArray)
            JsonArray(element.sortedBy { it.jsonObject["id"]?.jsonPrimitive?.content })
        else
            element
    )
}

@Serializable
data class Category(
    val id: String,

    @Serializable(with = CategorySetSerializer::class)
    val children: Set<Category> = emptySet()
)
Unfortunately, this solution seems too low level, brittle and JSON specific. What can be other approaches to enforce particular ordering of serialized Set’s elements? Ideally, I’d like to pass in the
@Serializable
my custom comparator when specific Set’s elements ordering is required.
e
something like this?
Copy code
@Serializable
data class Category(val id: String, @Serializable(SortedCategorySetSerializer::class) val children: Set<Category> = emptySet())

object SortedCategorySetSerializer : KSerializer<Set<Category>> {
    private val listSerializer = ListSerializer(Category.serializer())
    override val descriptor: SerialDescriptor = listSerializer.descriptor
    override fun serialize(encoder: Encoder, value: Set<Category>) {
        encoder.encodeSerializableValue(listSerializer, value.sortedBy { it.id })
    }
    override fun deserialize(decoder: Decoder): Set<Category> {
        return decoder.decodeSerializableValue(listSerializer).toSortedSet(compareBy { it.id })
    }
}
🙏 2
🙌 2