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

如何使用Gson TypeAdapter去除嵌套value值并映射至PoJo类?

刚好做过类似的需求,用Gson的TypeAdapter就能完美解决这个嵌套value的映射问题。核心思路就是自定义JSON的解析逻辑,跳过外层的嵌套对象,直接把value字段里的值绑定到PoJo的属性上。下面给你两种方案,按需选择:


方案一:针对特定PoJo的自定义TypeAdapter

这种方案适合只需要处理单个PoJo类的场景,逻辑更直观,也更容易控制细节。

首先先定义你的PoJo数据类:

data class PoJo(
    val SomeStringProperty: String,
    val SomeIntProperty: Int,
    val SomeBooleanProperty: Boolean
)

然后写对应的TypeAdapter,手动处理解析和序列化逻辑:

class PoJoTypeAdapter : TypeAdapter<PoJo>() {
    // 序列化:把PoJo转成带嵌套value的JSON格式(如果不需要序列化可以忽略这个方法)
    override fun write(out: JsonWriter, value: PoJo) {
        out.beginObject()
        // 给每个属性包装成带value的对象
        out.name("SomeStringProperty")
            .beginObject()
            .name("value").value(value.SomeStringProperty)
            .endObject()
        out.name("SomeIntProperty")
            .beginObject()
            .name("value").value(value.SomeIntProperty)
            .endObject()
        out.name("SomeBooleanProperty")
            .beginObject()
            .name("value").value(value.SomeBooleanProperty)
            .endObject()
        out.endObject()
    }

    // 核心:解析JSON,跳过外层对象直接取value值
    override fun read(`in`: JsonReader): PoJo {
        var stringVal: String? = null
        var intVal: Int? = null
        var boolVal: Boolean? = null

        `in`.beginObject()
        while (`in`.hasNext()) {
            when (`in`.nextName()) {
                "SomeStringProperty" -> {
                    `in`.beginObject()
                    while (`in`.hasNext()) {
                        if (`in`.nextName() == "value") {
                            stringVal = `in`.nextString()
                        } else {
                            // 忽略未知字段,避免解析失败
                            `in`.skipValue()
                        }
                    }
                    `in`.endObject()
                }
                "SomeIntProperty" -> {
                    `in`.beginObject()
                    while (`in`.hasNext()) {
                        if (`in`.nextName() == "value") {
                            intVal = `in`.nextInt()
                        } else {
                            `in`.skipValue()
                        }
                    }
                    `in`.endObject()
                }
                "SomeBooleanProperty" -> {
                    `in`.beginObject()
                    while (`in`.hasNext()) {
                        if (`in`.nextName() == "value") {
                            boolVal = `in`.nextBoolean()
                        } else {
                            `in`.skipValue()
                        }
                    }
                    `in`.endObject()
                }
                else -> {
                    // 忽略其他未知的JSON字段
                    `in`.skipValue()
                }
            }
        }
        `in`.endObject()

        // 这里可以根据业务需求添加非空校验,避免空指针
        return PoJo(
            stringVal ?: throw JsonSyntaxException("Missing required field: SomeStringProperty.value"),
            intVal ?: throw JsonSyntaxException("Missing required field: SomeIntProperty.value"),
            boolVal ?: throw JsonSyntaxException("Missing required field: SomeBooleanProperty.value")
        )
    }
}

方案二:通用嵌套Value适配器工厂(批量处理)

如果有很多类都是这种“每个字段嵌套在value对象里”的结构,写一个个TypeAdapter太麻烦,可以用TypeAdapterFactory做通用处理:

class NestedValueAdapterFactory : TypeAdapterFactory {
    override fun <T : Any?> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? {
        val elementType = type.type as? Class<T> ?: return null
        // 跳过基础类型和非自定义类(可选,根据需求调整)
        if (elementType.isPrimitive || elementType.name.startsWith("java.")) {
            return null
        }

        return object : TypeAdapter<T>() {
            override fun write(out: JsonWriter, value: T) {
                // 序列化时自动给每个字段包装成带value的对象
                out.beginObject()
                elementType.declaredFields.forEach { field ->
                    field.isAccessible = true
                    val fieldValue = field.get(value)
                    out.name(field.name)
                        .beginObject()
                        .name("value")
                    gson.toJson(fieldValue, field.type, out)
                    out.endObject()
                }
                out.endObject()
            }

            override fun read(`in`: JsonReader): T {
                // 反射创建PoJo实例(注意:需要确保类有无参构造函数,Kotlin data class默认提供)
                val instance = elementType.getDeclaredConstructor().newInstance()
                
                `in`.beginObject()
                while (`in`.hasNext()) {
                    val fieldName = `in`.nextName()
                    try {
                        val field = elementType.getDeclaredField(fieldName)
                        field.isAccessible = true
                        
                        `in`.beginObject()
                        while (`in`.hasNext()) {
                            if (`in`.nextName() == "value") {
                                // 用Gson自带的解析能力处理字段类型
                                val fieldValue = gson.fromJson(`in`, field.type)
                                field.set(instance, fieldValue)
                            } else {
                                `in`.skipValue()
                            }
                        }
                        `in`.endObject()
                    } catch (e: NoSuchFieldException) {
                        // 忽略不存在的字段
                        `in`.skipValue()
                    }
                }
                `in`.endObject()
                return instance
            }
        }
    }
}

如何注册使用

不管用哪种方案,都需要把适配器注册到Gson实例中才能生效:

方案一的注册方式

val gson = GsonBuilder()
    .registerTypeAdapter(PoJo::class.java, PoJoTypeAdapter())
    .create()

方案二的注册方式

val gson = GsonBuilder()
    .registerTypeAdapterFactory(NestedValueAdapterFactory())
    .create()

然后就可以正常解析和序列化了:

// 解析JSON
val json = """{ "SomeStringProperty": { "value":"StringValue"}, "SomeIntProperty": { "value": 1}, "SomeBooleanProperty": {"value": true} }"""
val pojo = gson.fromJson(json, PoJo::class.java)
println(pojo) // 输出:PoJo(SomeStringProperty=StringValue, SomeIntProperty=1, SomeBooleanProperty=true)

// 序列化(如果需要的话)
val serializedJson = gson.toJson(pojo)
println(serializedJson) // 回到原来的嵌套结构

注意事项

  1. 非空校验:上面的代码里加了非空异常,你可以根据业务需求改成默认值或者其他处理方式。
  2. 未知字段:代码里都做了忽略处理,避免因为JSON多了字段导致解析失败。
  3. 反射权限:通用方案用到了反射,Kotlin里需要确保字段可访问(field.isAccessible = true),如果是Android平台,注意API级别限制。

内容的提问来源于stack exchange,提问作者Lena Bru

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:02:24