如何使用Moshi库以不区分大小写方式反序列化JSON枚举?
问题描述
我们有如下示例JSON:
{ "source": "WIFI" }
当source的值为小写形式(比如"wifi")时,会抛出以下异常:
Exception in thread "main" com.squareup.moshi.JsonDataException: Expected one of [WIFI, CELL, GPS] but was 'wifi' at path $.locations[1].source
想了解Moshi是否有现成的注解或解决方案,能将小写字符串自动转换为对应的大写枚举,目前正在查看Moshi的EnumJsonAdapter.kt源码,考虑是否可通过修改它实现需求。
解决方案
1. 自定义大小写不敏感枚举Adapter(推荐)
无需修改Moshi源码,直接编写自定义Adapter处理枚举的大小写兼容:
class CaseInsensitiveEnumAdapter<T : Enum<T>> private constructor( private val enumClass: Class<T> ) : JsonAdapter<T>() { companion object { fun <T : Enum<T>> create(enumClass: Class<T>): CaseInsensitiveEnumAdapter<T> { return CaseInsensitiveEnumAdapter(enumClass) } } override fun fromJson(reader: JsonReader): T? { val value = reader.nextString() ?: return null return enumClass.enumConstants.firstOrNull { it.name.equals(value, ignoreCase = true) } ?: throw JsonDataException( "Expected one of ${enumClass.enumConstants.map { it.name }} but was '$value'" ) } override fun toJson(writer: JsonWriter, value: T?) { value?.let { writer.value(it.name) } } }
注册该Adapter到Moshi实例:
val moshi = Moshi.Builder() .add(YourEnum::class.java, CaseInsensitiveEnumAdapter.create(YourEnum::class.java)) .build()
2. 批量处理所有枚举的Adapter工厂
如果需要对项目中所有枚举生效,可以编写一个Adapter工厂:
class CaseInsensitiveEnumAdapterFactory : JsonAdapter.Factory { override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? { val rawType = Types.getRawType(type) return if (rawType.isEnum) { @Suppress("UNCHECKED_CAST") CaseInsensitiveEnumAdapter.create(rawType as Class<out Enum<*>>) as JsonAdapter<*> } else { null } } }
注册时直接添加工厂即可:
val moshi = Moshi.Builder() .add(CaseInsensitiveEnumAdapterFactory()) .build()
3. 扩展现有EnumJsonAdapter
针对你查看的EnumJsonAdapter,可以通过扩展方法实现大小写兼容,无需修改源码:
fun <T : Enum<T>> EnumJsonAdapter.Companion.create(enumClass: Class<T>, ignoreCase: Boolean): JsonAdapter<T> { val originalAdapter = create(enumClass) return object : JsonAdapter<T>() { override fun fromJson(reader: JsonReader): T? { val value = reader.nextString() ?: return null return if (ignoreCase) { enumClass.enumConstants.firstOrNull { it.name.equals(value, ignoreCase = true) } ?: throw JsonDataException("Expected one of ${enumClass.enumConstants.map { it.name }} but was '$value'") } else { originalAdapter.fromJson(reader) } } override fun toJson(writer: JsonWriter, value: T?) { originalAdapter.toJson(writer, value) } } }
使用时指定忽略大小写:
val moshi = Moshi.Builder() .add(YourEnum::class.java, EnumJsonAdapter.create(YourEnum::class.java, ignoreCase = true)) .build()
内容的提问来源于stack exchange,提问作者Hossain Khan
相关产品推荐
相关产品推荐

