You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 05:58:37