如何配置Gson使继承类导出JSON时父类字段优先或自定义顺序?
解决Gson导出JSON时父类字段优先/自定义字段顺序的问题
你可以通过两种方式实现Gson序列化时父类字段优先,或者自定义字段输出顺序:
方案一:用@SerializedName指定序列化优先级
Gson的@SerializedName注解支持通过serialize参数设置字段的序列化顺序,数值越小,字段越先被序列化。给父类字段设置更小的数值,子类字段设置更大的数值即可。
代码示例:
父类Foo:
import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; public abstract class Foo { @Expose @SerializedName(value = "name", serialize = 1) private String name; // 构造器、getter/setter 自行补充 }
子类Bar:
import com.google.gson.annotations.Expose; import com.google.gson.annotations.SerializedName; public class Bar extends Foo { @Expose @SerializedName(value = "value", serialize = 2) private int value; // 构造器、getter/setter 自行补充 }
初始化Gson并序列化:
Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() // 启用@Expose注解过滤 .create(); Bar bar = new Bar(); bar.setName("test"); bar.setValue(123); String json = gson.toJson(bar); // 输出结果:{"name":"test","value":123}
方案二:自定义TypeAdapterFactory统一处理继承结构
如果需要批量处理大量继承类,不想逐个字段加注解,可以自定义TypeAdapterFactory,强制先序列化父类字段,再序列化子类字段。
代码示例:
自定义Factory:
import com.google.gson.*; import com.google.gson.reflect.TypeToken; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class ParentFirstTypeAdapterFactory implements TypeAdapterFactory { @Override public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { Class<?> clazz = type.getRawType(); // 仅处理非Object的子类 if (clazz.getSuperclass() == Object.class) { return null; } final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, type); final TypeAdapter<JsonElement> elementAdapter = gson.getAdapter(JsonElement.class); return new TypeAdapter<T>() { @Override public void write(JsonWriter out, T value) throws Exception { JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); JsonObject orderedObject = new JsonObject(); // 先添加父类的@Expose字段 List<Field> parentFields = getAllFields(clazz.getSuperclass()); for (Field field : parentFields) { String fieldName = getSerializedName(field); if (jsonObject.has(fieldName)) { orderedObject.add(fieldName, jsonObject.get(fieldName)); jsonObject.remove(fieldName); } } // 再添加子类剩余的字段 orderedObject.addAll(jsonObject); elementAdapter.write(out, orderedObject); } @Override public T read(JsonReader in) throws Exception { return delegate.read(in); } // 递归获取类的所有@Expose字段(包括私有) private List<Field> getAllFields(Class<?> clazz) { List<Field> fields = new ArrayList<>(); while (clazz != Object.class) { for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Expose.class)) { fields.add(field); } } clazz = clazz.getSuperclass(); } return fields; } // 获取字段的序列化名称,优先用@SerializedName的值,没有则用字段名 private String getSerializedName(Field field) { SerializedName annotation = field.getAnnotation(SerializedName.class); return annotation != null ? annotation.value() : field.getName(); } }; } }
使用自定义Factory:
Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .registerTypeAdapterFactory(new ParentFirstTypeAdapterFactory()) .create(); Bar bar = new Bar(); bar.setName("test"); bar.setValue(123); String json = gson.toJson(bar); // 输出结果:{"name":"test","value":123}
注意事项
- 方案一简单直观,适合字段数量少、需要精准控制单个字段顺序的场景;方案二更适合批量处理多个继承类的情况。
- Gson默认的字段顺序依赖JVM反射获取字段的顺序,不同环境可能有差异,显式指定顺序能保证输出稳定。
内容的提问来源于stack exchange,提问作者Dylan Trenti
相关产品推荐
相关产品推荐

