首页 > 解决方案 > 使用 moshi 通过枚举序列化泛型

问题描述

背景

使用 Moshi,我想为枚举创建通用适配器,它指向我想使用枚举类型的类,因为在元素的更下方,我有复杂的结构,进一步归结为不同的类型。

是否可以通过 Moshi 以这种方式进行序列化?

我试图制作一个可以处理任何类型的通用适配器,Attempt但到目前为止我只有 clazz 对象而不是实际的 T。

示例 Json

{
  "items": [
    {
      "type": "A",
      "apple": "123 Apples"
    },
    {
      "type": "B",
      "organge": "Banana 12",
      "info": {}
    },
    {
      "type": "C",
      "grapes": "Green",
      "quantity": {
        "inStock": "12",
        "offShelf": "12"
      }
    }
  ]
}

类结构

classs FruitResponse(val items: List<FruitTypes>)


@JsonClass(generateAdapter = false)
enum class FruitType(val clazz: Class<*>) {

    A(Apple::class.java),

    B(Banana::class.java),

    C(Grapes::class.java)
}

试图

class FruitsAdapter<T : Enum<*>>(enumType: Class<T>) : JsonAdapter<T>() {

    private val nameStrings: Array<String?>
    private val nameConstantMap: MutableMap<String, T>

    init {
        try {
            val constants = enumType.enumConstants
            nameStrings = arrayOfNulls<String>(constants?.size ?: 0)
            nameConstantMap = LinkedHashMap()
            constants?.forEachIndexed { index, constant ->
                val annotation = enumType.getField(constant.name).getAnnotation(Json::class.java)
                val name = annotation?.name ?: constant.name
                nameConstantMap[name] = constant
                nameStrings[index] = name
            }
        } catch (e: NoSuchFieldException) {
            throw AssertionError("Missing field in ${enumType.name}")
        }
    }

    @Throws(IOException::class)
    override fun fromJson(reader: JsonReader): T {
        val name = reader.nextString()
        val constant = nameConstantMap[name]
        if (constant != null) return constant
        throw JsonDataException(
            "Expected one of ${Arrays.asList(*nameStrings)} " +
                "but was $name at path ${reader.path}"
        )
    }

    @Throws(IOException::class)
    override fun toJson(writer: JsonWriter, value: T?) {
        val newValue = nameConstantMap.filter { value == it.value }.map { it.key }.firstOrNull()
        if (newValue != null) writer.value(newValue) else writer.nullValue()
    }
}


// Usage
  val moshiAdapter = Moshi.Builder()
        .add(
            FruitType::class.java,
            FruitsAdapter(FruitType::class.java)
        ).build()

标签: androidkotlingenericsmoshi

解决方案


看看PolymorphicJsonAdapterFactory可以在moshi-adapters工件中找到。

如果您想避免手动编写多态适配器的样板,还有moshi-sealed 。


推荐阅读