使用 moshi 通过枚举序列化泛型
Serialize Generics via enum using moshi
背景
使用 Moshi,我想为指向 class 的枚举创建通用适配器
我想使用枚举类型,因为在元素的下方我有复杂的结构,进一步归结为不同的类型。
是否可以通过via 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"
}
}
]
}
Class结构
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()
看看PolymorphicJsonAdapterFactory which can be found in moshi-adapters神器
如果您想避免手动编写多态适配器的样板,还有 moshi-sealed。
背景
使用 Moshi,我想为指向 class 的枚举创建通用适配器 我想使用枚举类型,因为在元素的下方我有复杂的结构,进一步归结为不同的类型。
是否可以通过via 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"
}
}
]
}
Class结构
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()
看看PolymorphicJsonAdapterFactory which can be found in moshi-adapters神器
如果您想避免手动编写多态适配器的样板,还有 moshi-sealed。