Currently having an issue with PolymorphicJsonAdapterFactory and Kotlins sealed class. I have an API that that returns polymorphic home components and I am trying to parse and create polymorphic object using Moshi in Kotlin but I getting following error:
  Caused by: java.lang.IllegalArgumentException: Cannot serialize abstract class home.HomeComponent
for class home.HomeComponent
for java.util.List<home.HomeComponent> components
for class home.HomeContent
Code:
sealed class HomeComponent(@Json(name = "_type") val type: HomeContentType) {
data class BannerComponent(@field:Json(name = "_id")
                               val id: String,
                               val image: String) : HomeComponent(HomeContentType.banner)
data class SpecialProductsComponent(@field:Json(name = "_id")
                                        val id: String,
                                        val style: List<Product>) : HomeComponent(HomeContentType.specialProducts)
data class CarouselBannerComponent(@field:Json(name = "_id")
                                       val id: String,
                                       val style: String,
                                       val images: List<String>) : HomeComponent(HomeContentType.carousel)
}
enum class HomeContentType {
    @Json(name = "banner")banner,
    @Json(name = "products")Products,
    @Json(name = "carousel")carousel
}
ApiFetcher Class:
class HomeApiFetcher(private val backend: HomeContentBackend) : HomeFetcher {
    companion object {
        fun from(retrofit: Retrofit,
                 moshi: Moshi): HomeApiFetcher {
            val moshi = moshi.newBuilder()
                    .add(
                            PolymorphicJsonAdapterFactory.of(HomeComponent::class.java, "_type")
                                    .withSubtype(HomeComponent.BannerComponent::class.java, HomeContentType.banner.name)
                                    .withSubtype(HomeComponent.SpecialProductsComponent::class.java, HomeContentType.specialProducts.name)
                                    .withSubtype(HomeComponent.CarouselBannerComponent::class.java, HomeContentType.carousel.name))
                    .add(KotlinJsonAdapterFactory())
                    .build()
            val homeBackend = retrofit
                    .newBuilder()
                    .addConverterFactory(MoshiConverterFactory.create(moshi))
                    .build()
                    .create(HomeContentBackend::class.java)
            return HomeApiFetcher(homeBackend)
        }
    }
    override fun getHomeContent(): Single<HomeContent> {
        return backend.load()
    }
}
JSON from API:
{
    "common": {
        "background": "",
        "backgroundType": "NONE",
        "floatingImgs": {
            "left": "",
            "right": ""
        },
        "actualDate": "2019-09-23T15:03:20.8626882Z"
    },
    "components": [{
            "image": "http://image1.url",
            "_id": "carousel1",
            "style": "SLIDE",
            "_type": "banner"
        },
        {
            "products": [],
            "_id": "id1",
            "style": "CARD",
            "_type": "products"
        },
        {
            "images": ["http://image1.url",
                "http://image1.url",
                "http://image1.url"
            ],
            "_id": "carousel1",
            "style": "SLIDE",
            "_type": "carousel"
        }
    ]
}
I am not sure what's wrong in my code. I am getting Cannot serialize abstract class HomeComponent error
I've changed @field:Json to Json, Products to products and SpecialProductsComponent fields. So finally I got:
sealed class HomeComponent(@Json(name = "_type") val type: HomeContentType) {
    data class BannerComponent(
        @Json(name = "_id")
        val id: String,
        val image: String
    ) : HomeComponent(HomeContentType.banner)
    data class SpecialProductsComponent(
        @Json(name = "_id")
        val id: String,
        val style: String,
        val products: List<Product>
    ) : HomeComponent(HomeContentType.products)
    data class CarouselBannerComponent(
        @Json(name = "_id")
        val id: String,
        val style: String,
        val images: List<String>
    ) : HomeComponent(HomeContentType.carousel)
}
enum class HomeContentType {
    @Json(name = "banner")
    banner,
    @Json(name = "products")
    products,
    @Json(name = "carousel")
    carousel
}
and now it seems that all works fine. I checked it with following code:
fun main() {
    val moshi = Moshi.Builder()
        .add(
            PolymorphicJsonAdapterFactory.of(HomeComponent::class.java, "_type")
                .withSubtype(HomeComponent.BannerComponent::class.java, HomeContentType.banner.name)
                .withSubtype(HomeComponent.SpecialProductsComponent::class.java, HomeContentType.products.name)
                .withSubtype(HomeComponent.CarouselBannerComponent::class.java, HomeContentType.carousel.name)
        )
        .add(KotlinJsonAdapterFactory())
        .build()
    val json = """
[
  {
    "image": "http://image1.url",
    "_id": "carousel1",
    "style": "SLIDE",
    "_type": "banner"
  },
  {
    "products": [],
    "_id": "id1",
    "style": "CARD",
    "_type": "products"
  },
  {
    "images": [
      "http://image1.url",
      "http://image1.url",
      "http://image1.url"
    ],
    "_id": "carousel1",
    "style": "SLIDE",
    "_type": "carousel"
  }
]
    """.trimIndent()
    val adapter =
        moshi.adapter<List<HomeComponent>>(Types.newParameterizedType(List::class.java, HomeComponent::class.java))
    val result = adapter.fromJson(json)
    println(result)
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With