Reputation: 111
I'm using Moshi and I have the following data classes
@JsonClass(generateAdapter = true)
data class A (
@Json(name = "_id")
val id: String?,
@Json(name = "b")
val b: B? = null
)
@JsonClass(generateAdapter = true)
data class B (
@Json(name = "_id")
val id: String?,
@Json(name = "foo")
val foo: String? = null
@Json(name = "c")
val c: C? = null
)
@JsonClass(generateAdapter = true)
data class C (
@Json(name = "_id")
val id: String?,
@Json(name = "bar")
val bar: String? = null
)
My API sometimes returns the object as just an ID, while other times it returns it as the actual object. For example, sometimes when I get the object A it will return
{
_id: "111111111",
b: {
_id: "222222222",
foo: "foo",
c: {
_id: "333333333",
bar: "bar"
}
}
}
but other times it may return
{
_id: "111111111",
b: "222222222"
}
or
{
_id: "111111111",
b: {
_id: "222222222",
foo: "foo",
c: "333333333"
}
}
As we see, it may return a string representing the object, or the populated object itself. How do I create a custom Moshi adapter to handle this? If it returns an id representing the object, I'd like it to create an object with just the id populated and the rest of the fields set to null.
I tried creating a custom adapter like so
class bAdapter {
@FromJson
fun fromJson(b: Any): B {
return when (b) {
is String -> B(b)
else -> b as B
}
}
}
but I get the error
com.squareup.moshi.JsonDataException: java.lang.ClassCastException: com.squareup.moshi.LinkedHashTreeMap cannot be cast to com.example.B
Upvotes: 3
Views: 1872
Reputation: 111
If anyone is having the same problem, I solved it by using the nifty @JsonQualifier annotation like so:
@Retention(AnnotationRetention.RUNTIME)
@JsonQualifier
internal annotation class ObjectString
class ObjectStringAdapter {
@FromJson
@ObjectString
fun fromJson(id: String): Object {
return Object(id)
}
@ToJson
fun toJson(@ObjectString obj: Object): String {
return obj.toString()
}
}
class ObjectAdapter {
@FromJson
fun fromJson(jsonReader: JsonReader, @ObjectString stringAdapter: JsonAdapter<Object>, defaultAdapter: JsonAdapter<Object>): Object {
return when (JsonReader.Token.STRING) {
jsonReader.peek() -> {
stringAdapter.fromJson(jsonReader)
}
else -> {
defaultAdapter.fromJson(jsonReader)
}
}!!
}
}
where Object
is your class.
Basically created two adapters, one for a string and one for an object, and used jsonReader.peek() to peek at the value and determine at runtime which one to use depending on the type.
For reference: Moshi LocalDateTime adapter with multiple format
Upvotes: 1