首页 > 解决方案 > 如何在实现 GSON 的数据类中创建计算属性?

问题描述

背景:GSON、Kotlin、改造

我正在写一个餐厅应用程序。在主页中,用户可以加载餐厅品牌列表。每个品牌最多可以有 3 种美食类型,第一种是非空的,接下来的两种是可空的。每种美食类型都在 CuisineType 枚举类中。

我想做的是创建一个像这样的连接字符串:美食类型 1.标题 + 美食类型 2?.标题 + 美食类型 3?.标题 = 组合美食。这可以使一个 textView 中的所有美食都以中文显示。为了做到这一点,我创建了一个助手类。在这个助手类中,如果 Brand 中的 CuisineType 无法映射任何枚举成员,它将显示 Brand JSON 中的原始名称(以防服务器错误)。我尝试了下面注释掉的三种解决方案,但它们都不起作用。很多帮助将不胜感激。提前致谢!

data class Brand(
    @SerializedName("id")
    val id: Int,
    @SerializedName("name_en")
    val nameEN: String?,

    @SerializedName("cuisine_1")
    val cuisineType1: String,
    @SerializedName("cuisine_2")
    val cuisineType2: String?,
    @SerializedName("cuisine_3")
    val cuisineType3: String?,

    /*Solution 1(not working):
        val combinedCuisines = CombineCuisineHelper.combineCuisines(cuisineType1, cuisineType2, cuisineType3)
    ***java.lang.IllegalArgumentException: Unable to create converter for class
    */

    /*Solution 2(not working):
        @Transient
        val combinedCuisines = CombineCuisineHelper.combineCuisines(cuisineType1, cuisineType2, cuisineType3)
    ***combinedCuisines = null after network call in fragment
    */
) {
    /* Solution 3(not working):
        val combinedCuisines: String
            get() = CombineCuisineHelper.combineCuisines(cuisineType1, cuisineType2, cuisineType3)
        ***problem with GSON, I can only map the @SerializedName from the Cuisine enum class and will only run the illegal argument solution from the CombineCuisineHelper. For example, get hong_kong_style from the JSON brand but it will not convert to HongKongStyle and map to its title.
    */
}

//It should be a long list but I shortened it.
enum class CuisineType {
    @SerializedName("chinese")
    Chinese,
    @SerializedName("hong_kong_style")
    HongKongStyle,
    @SerializedName("cantonese")
    Cantonese,

    val title: Double
    get() {
        return when (this) {
            Chinese       -> "中菜"
            HongKongStyle -> "港式"
            Cantonese     -> "粵式"
}

class CombineCuisineHelper {
    companion object {
        fun combineCuisines(cuisineSubtype1: String, cuisineSubtype2: String?, cuisineSubtype3: String?): String {
            val combinedSubtypes = ArrayList<String?>()
            combinedSubtypes += try {
                CuisineSubtype.valueOf(cuisineSubtype1).title
            } catch (e: IllegalArgumentException) {
                cuisineSubtype1
            }
            if (cuisineSubtype2 != null) {
                combinedSubtypes += try {
                    CuisineSubtype.valueOf(cuisineSubtype2).title
                } catch (e: IllegalArgumentException) {
                    cuisineSubtype2
                }
            }
            if (cuisineSubtype3 != null) {
                combinedSubtypes += try {
                    CuisineSubtype.valueOf(cuisineSubtype3).title
                } catch (e: IllegalArgumentException) {
                    cuisineSubtype3
                }
            }
}

标签: androidjsonkotlingson

解决方案


第一种和第二种解决方案都不好,因为数据可能在初始化时还没有准备好。第三种解决方案是我们可以继续进行的解决方案:

        val combinedCuisines: String
            get() = CombineCuisineHelper.combineCuisines(cuisineType1, cuisineType2, cuisineType3)

s对于SerializedName枚举常量没有用,也不会像您期望的那样为您工作。所以valueOf枚举的方法不会找到像这样的文字的值,"hong_kong_style"并且会抛出异常。

您可以像这样在枚举类中创建自己的辅助方法:

enum class CuisineType {
    Chinese,
    HongKongStyle,
    Cantonese;

    val title: String
        get() {
            return when (this) {
                Chinese -> "中菜"
                HongKongStyle -> "港式"
                Cantonese -> "粵式"
            }
        }

    companion object {
        //Note this helper method which manually maps string values to enum constants:
        fun enumValue(title: String): CuisineType = when (title) {
            "chinese" -> Chinese
            "hong_kong_style" -> HongKongStyle
            "cantonese" -> Cantonese
            else -> throw IllegalArgumentException("Unknown cuisine type $title")
        }
    }
}

然后使用这个新方法而不是枚举自己的valueOf方法:

            val combinedSubtypes = ArrayList<String?>()
            combinedSubtypes += try {
                CuisineType.enumValue(cuisineSubtype1).title
            } catch (e: IllegalArgumentException) {
                cuisineSubtype1
            }
            //continued...

推荐阅读