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

如何让Gson正确处理Groovy Trait的序列化?

解决Gson序列化Groovy Trait属性的字段名问题

问题原因

Groovy编译Trait时,会将Trait中的属性转换为带全限定名前缀的私有字段(比如com_company_project_traits_v2_TOpeningTimes__isOpen),Gson默认直接序列化这些生成的字段名,导致输出不符合预期。

解决方案

方案1:给Trait属性添加Gson序列化注解

直接在Trait的属性上标注@SerializedName,指定序列化后的字段名,让Gson忽略生成的前缀字段:

修改TOpeningTimes Trait:

package com.company.project.traits.v2

import com.company.project.model.OpeningTime
import com.google.gson.annotations.SerializedName

trait TOpeningTimes {
    @SerializedName("openingTimes")
    List<OpeningTime> openingTimes
    
    @SerializedName("isOpen")
    boolean isOpen
}

这种方法简单直接,无需额外编写适配器代码,适合可以修改Trait源码的场景。

方案2:自定义TypeAdapterFactory适配Trait属性

如果无法修改Trait源码,可通过自定义Gson的TypeAdapterFactory,自动识别Trait生成的前缀字段并替换为原始属性名:

1. 实现TraitPropertyAdapterFactory

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class TraitPropertyAdapterFactory implements TypeAdapterFactory {

    @SuppressWarnings("unchecked")
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type);
        Class<?> clazz = type.getRawType();

        Map<String, String> traitPropertyMap = new HashMap<>();
        // 遍历类实现的所有Trait,收集前缀字段与原始属性名的映射
        for (Class<?> iface : clazz.getInterfaces()) {
            if (iface.isAnnotationPresent(groovy.lang.Trait.class)) {
                String prefix = iface.getName().replace('.', '_') + "__";
                for (Field field : iface.getDeclaredFields()) {
                    traitPropertyMap.put(prefix + field.getName(), field.getName());
                }
            }
        }

        if (traitPropertyMap.isEmpty()) {
            return delegate;
        }

        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                // 先序列化到JsonElement,再替换字段名
                com.google.gson.JsonElement jsonElement = delegate.toJsonTree(value);
                if (jsonElement.isJsonObject()) {
                    com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject();
                    for (Map.Entry<String, String> entry : traitPropertyMap.entrySet()) {
                        if (jsonObject.has(entry.getKey())) {
                            com.google.gson.JsonElement element = jsonObject.get(entry.getKey());
                            jsonObject.remove(entry.getKey());
                            jsonObject.add(entry.getValue(), element);
                        }
                    }
                }
                gson.toJson(jsonElement, out);
            }

            @Override
            public T read(JsonReader in) throws IOException {
                // 反序列化时反向替换字段名
                com.google.gson.JsonElement jsonElement = gson.fromJson(in, com.google.gson.JsonElement.class);
                if (jsonElement.isJsonObject()) {
                    com.google.gson.JsonObject jsonObject = jsonElement.getAsJsonObject();
                    for (Map.Entry<String, String> entry : traitPropertyMap.entrySet()) {
                        if (jsonObject.has(entry.getValue())) {
                            com.google.gson.JsonElement element = jsonObject.get(entry.getValue());
                            jsonObject.remove(entry.getValue());
                            jsonObject.add(entry.getKey(), element);
                        }
                    }
                }
                return delegate.fromJsonTree(jsonElement);
            }
        };
    }
}

2. 注册适配器到Gson

创建Gson实例时注册这个工厂:

Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(new TraitPropertyAdapterFactory())
        // 保留已有的几何类型序列化配置
        .create();

这种方法无需修改原有业务代码,适合Trait无法修改的场景,同时支持序列化和反序列化双向处理。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:13:24