jean
07/18/2023, 11:30 AMvalue
. It can be a string, double, boolean or a list of string.
[
{
"name": "travel_money_active",
"value": true,
"description": "Travel money is set to active"
}, ...
]
I have created this types for it
@Serializable
abstract class Setting<T> {
abstract val name: String
abstract val value: T
abstract val description: String?
}
@Serializable
class BooleanSetting(
override val name: String,
override val value: Boolean,
override val description: String? = null,
): Setting<Boolean>()
...
val settingSerializerModule = SerializersModule {
polymorphic(baseClass = Setting::class, baseSerializer = null) {
subclass(BooleanSetting::class)
subclass(DoubleSetting::class)
subclass(StringSetting::class)
subclass(StringListSetting::class)
}
}
...
install(ContentNegotiation) {
json(
Json {
serializersModule = settingSerializerModule
prettyPrint = true
isLenient = true
},
)
}
In one test, I use the following
val settings = Json.decodeFromString<List<Setting<*>>>(response)
assertTrue(settings.size == 7)
but I get Star projections in type arguments are not allowed, but had Setting<*>
What can I do to make it work?Adam S
07/18/2023, 11:39 AMvalue
always be a primitive?jean
07/18/2023, 11:40 AMjean
07/18/2023, 11:44 AMjean
07/18/2023, 11:50 AMAdam S
07/18/2023, 11:50 AMval value: T
with val value: JsonPrimitive
.
Pro: it’s quick and simple, and the decoding becomes a case of some extra Kotlin code (which is generally easier to write and debug than custom KxS serializers)
Con: It’s slightly less type-strict, so it would allow for decoding ‘invalid’ types
2. convert Setting to a sealed interface, and create specific subtypes for each primitive type of value. Create a custom JsonContentPolymorphicSerializer to select the correct subclass.
Pro: much more explicit and strict
Con: Verbose and repetitive, and is not easy to scale if there are ever multiple generic args
3. create a custom sealed interface SettingValue
type and replace val value: T
with val value: SettingValue
. Create subtypes as value classes for each allowed subtype. Create a custom JsonContentPolymorphicSerializer to select the correct value type.
@Serializable(with = SettingValueSerializer::class)
sealed interface SettingValue
value class SettingValueString(val value: String): SettingValue, CharSequence by value
value class SettingValueDouble(val value: Double): SettingValue
...
This is basically a manual version of JsonPrimitive.
Pro: More explicit, easier to scale/adapt, can re-use the value classes in multiple hierarchies (for example, if one JSON value can be a string/boolean, then create a new sealed interface and make SettingValueString & SettingValueBoolean extend from it
Con: It can be annoying to work with value classes in Kotlin (even if you do some interface delegation), so you might end up with as much Kotlin conversion code as if you used a JsonPrimitive.jean
07/18/2023, 11:53 AMisString
was making it difficult to use, I’ll check the other options. Thanks for the help!Adam S
07/18/2023, 11:54 AMAdam S
07/18/2023, 11:54 AMjean
07/18/2023, 11:57 AM