如何使用Moshi解析Kotlin中的多态对象

时间:2019-09-24 00:11:32

标签: json kotlin jsonparser moshi

当前与PolymorphicJsonAdapterFactory和Kotlins密封类有关。我有一个可返回多态家庭组件的API,我正在尝试使用Kotlin中的Moshi解析并创建多态对象,但出现以下错误:

  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

代码:

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 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()
    }
}

来自API的JSON:

{
    "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"
        }
    ]
}

我不确定我的代码有什么问题。我收到Cannot serialize abstract class HomeComponent错误

1 个答案:

答案 0 :(得分:2)

我已将@field:Json更改为Json,将Products更改为productsSpecialProductsComponent字段。所以最后我得到了:

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
}

,现在看来一切正常。我用以下代码检查了它:

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)
}