如何让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
相关产品推荐
相关产品推荐

