So, it seems that there are two possible approaches to this, both of which are mildly clunky:
1.
@Serializable(with=...)
requires a class literal, which forces a custom serializer for every tiny type, because deserialization requires it to construct an instance of the concrete tiny type.
2. Contextual + SerializerModule, as suggested by
@Javier, which requires every field the has a tiny type to be annotated with
@Contextual
, plus listing the serializer in a SerializerModule and passing the module to the Json formatter. This approach does give the option of having just one generic custom serializer because we can call a method and pass parameters to customize the way it works.
Overall, I suspect option one is the least distasteful, as it keeps the boilerplate confined to the declaration of the tiny type. e.g.
@Serializable(with=SortCodeSerializer::class)
data class SortCode(override val value: String) : Tiny<String>()
object SortCodeSerializer: TinySerializer<String, SortCode>(::SortCode, serializer())