Why does Moshi not use my custom adapter?

Viewed 1763

We have a polymorphic behaviour, and to cater it with deserialisation, we have written a custom Moshi adapter for it.

@JsonClass(generateAdapter = true)
open class Field
@JsonClass(generateAdapter = true)
data class Field1 (
    @Json(name = "name")
    val name: String
): Field()
@JsonClass(generateAdapter = true)
data class Field2 (
    @Json(name = "address")
    val address: String
): Field()

The above is a very simplified version of what we are doing. We would like to use Moshi generated adapter for child classes (Field1 and Field2) and this custom adapter only delegates the fromJson based on the available fields. Here is the custom adapter for Field:

class FieldAdapter {
    var moshi = Moshi.Builder().build()
    @ToJson
    fun toJson(field: Field): String {
        return ""
    }

    @FromJson
    fun fromJson(json: String): Field {
        val jsonObject = gson.fromJson(json, JsonObject::class.java)
        var field: Field? = null

        if (jsonObject.has("name")) {
            val field1JsonAdapter: JsonAdapter<Field1> = moshi.adapter(Field1::class.java)
            field = field1JsonAdapter.fromJson(json)
        } else if (jsonObject.has("address")) {
            val field2JsonAdapter: JsonAdapter<Field2> = moshi.adapter(Field2::class.java)
            field = field2JsonAdapter.fromJson(json)
        }

        field?.let {
            return it
        }

        throw JsonDataException("Not a valid field JSON")
    }
}

The following test does not work:

@Test
fun testField1Serialization() {
    val json = MockResponseFileReader("field1.json").content

    val moshi = Moshi.Builder().add(FieldAdapter()).build()
    val adapter = moshi.adapter(Field::class.java)

    val field = adapter.fromJson(json)

    val field1 = field as Field1
    assertEquals("abc", field1.name)
}

The above test does not call the fromJson of our custom adapter, however if we change val adapter = moshi.adapter(Field::class.java) to val adapter = FieldAdapter() (to hardcode the custom adapter), it works.

Can you please guide what are we doing wrong?

Any help would be appreciated.

TIA

2 Answers

For anyone facing the same issue, just implement JsonAdapter (toJson and fromJson methods) in your custom JSON adapater and it will just work.

Unfortunately, I still don't have an answer to why the adapter from original question would not work since this is how it is done in their official documentation.

Moshi’s adapters are ordered by precedence, so you always want to add the Kotlin adapter after your own custom adapters. Otherwise the KotlinJsonAdapterFactory will take precedence and your custom adapters will not be called.

Moshi.Builder()
       .add(MyCustomAdapter())
       .add(KotlinJsonAdapterFactory())
       .build()
Related