Philip Dukhov
02/03/2023, 4:11 AMUser
with many fields and class CurrentUser
which should have all the fields from User
plus some other keys.
That's how I'm receiving it from the server: CurrentUser
just has some additional fields that should be presented in json.
e.g. user is {"name":"Phil", "avatarUrl":"<https://some.url>"}
and current user is {"name":"Phil", "avatarUrl":"<https://some.url>", "token":"lorem ipsum"}
I could've inherit, but it'd require me to repeat all of user fields.
I solved it by making a custom adapter factory that reads extra properties from json and than pass it to UserAdapter
to fill it. But it takes a lot of boilerplate code, maybe there's some built in solution for such case? I'd like to add some annotation to user
property so codegen would write something similar for me. Or is there a way to build a general factory for such cases?
data class CurrentUser(
val token: String,
val user: User,
) {
companion object {
private const val tokenKey = "token"
val adapterFactory = JsonAdapter.Factory { type, _, moshi ->
if (type != CurrentUser::class.java) {
return@Factory null
}
val userJsonAdapter = moshi.adapter(User::class.java)
object : JsonAdapter<CurrentUser>() {
override fun fromJson(reader: JsonReader): CurrentUser? {
val jsonValue = reader.readJsonValue()
?: return null
if (jsonValue !is Map<*, *>) {
throw IllegalStateException("${this@Companion} readJsonValue is not map: $jsonValue")
}
return CurrentUser(
token = jsonValue[tokenKey] as? String
?: throw IllegalStateException("$tokenKey not found in $jsonValue"),
user = userJsonAdapter.fromJsonValue(jsonValue)
?: throw IllegalStateException("Failed to parse user from $jsonValue"),
)
}
override fun toJson(writer: JsonWriter, value: CurrentUser?) {
value?.user
?.let(userJsonAdapter::toJsonValue)
?.asMap()
?.plus(mapOf(tokenKey to value.token))
?.let(writer::jsonValue)
}
}
}
}
}
Zac Sweers
02/03/2023, 4:17 AMColton Idle
02/03/2023, 4:19 AMUserResponseDTO
and then in my domain or something, I would just check if token
exists (or some other invariant) and then map to a different domain model (User
or CurrentUser
) based on that invariant. Seems dead simple to me that way (which is nice?)Philip Dukhov
02/03/2023, 9:26 AM"{\"type\":1,\"rawJson\":{\"a\":2,\"b\":3,\"c\":[1,2,3]}}"
but "{\"type\":1,\"a\":2,\"b\":3,\"c\":[1,2,3]}"
into some object which will be a composition of other objects.Colton Idle
02/03/2023, 4:16 PMtype
field then you could use PolymorphicTypeAdapter from moshi to go into a User or CurrentUser pretty easily (if I'm understanding the question correctly).