简体   繁体   中英

Serialize Generics via enum using moshi

Background

Using Moshi, I want to create generic adapter for enum that points to a class I want to use enum type because further down the elements i have complex structure which further boils to different types.

Is it possible to serialize this way via via Moshi?

I tried to make a generic adapter that can handle any type in Attempt but so far I only have the clazz object not the actual T.

Sample Json

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

Class Structure

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

Attempt

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

Take a look at PolymorphicJsonAdapterFactory which can be found in moshi-adapters artifact.

There's also moshi-sealed if you want avoid the boilerplate of manually writing a polymorphic adapter.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM