Gson反序列化对象异常:JavaFX场景下序列化出现额外键问题
这个问题我做JavaFX项目时也踩过一模一样的坑!核心原因是Gson默认会序列化ObservableArrayList的所有内部成员,包括它自带的helper和observable这些用于JavaFX响应式更新的字段——这些完全不属于你的业务数据,反序列化时Gson找不到对应的类定义,自然就抛出异常了。
下面给你几个实用的解决办法,按推荐程度排序:
1. 自定义TypeAdapter(最推荐,一劳永逸)
写一个专门的TypeAdapter,告诉Gson只处理ObservableList里的实际业务元素,忽略JavaFX的内部字段:
import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class ObservableListTypeAdapter<T> extends TypeAdapter<ObservableList<T>> { private final TypeAdapter<List<T>> listAdapter; public ObservableListTypeAdapter(TypeAdapter<List<T>> listAdapter) { this.listAdapter = listAdapter; } @Override public void write(JsonWriter out, ObservableList<T> value) throws IOException { // 把ObservableList转成普通ArrayList再序列化,只保留业务元素 listAdapter.write(out, new ArrayList<>(value)); } @Override public ObservableList<T> read(JsonReader in) throws IOException { // 反序列化成普通List后,再转成ObservableList供JavaFX使用 List<T> list = listAdapter.read(in); return FXCollections.observableArrayList(list); } }
然后创建Gson实例时注册这个Adapter:
Gson gson = new GsonBuilder() .registerTypeAdapter(ObservableList.class, new ObservableListTypeAdapter<>(new Gson().getAdapter(List.class))) .create();
这样不管你哪里用到ObservableList,Gson都会正确处理,不会再出现那些奇怪的额外字段。
2. 序列化前临时转回普通List(快速应急)
如果你的ReportTemplate里原本用的是普通List,只是绑定控件时转成了ObservableArrayList,那可以在序列化前临时把字段值转回普通List:
// 假设ReportTemplate有个getItems()/setItems()方法 ReportTemplate template = ...; // 先把ObservableList转成普通ArrayList List<YourDataType> plainItems = new ArrayList<>(template.getItems()); // 临时替换成普通List template.setItems(plainItems); // 执行序列化 String json = gson.toJson(template); // 如果需要保留原ObservableList,序列化后再转回去 template.setItems(FXCollections.observableArrayList(plainItems));
这个方法不用写额外的类,适合临时快速解决问题,但如果多处需要序列化,还是自定义Adapter更省心。
3. 用@Expose注解过滤(不推荐,局限性大)
如果ReportTemplate直接用ObservableList作为字段,你可以配置Gson只序列化带@Expose注解的字段,但这个方法需要你能控制ObservableList的代码——显然不可能,因为它是JavaFX的内置类,所以这个方法基本用不上,只作为补充思路提一下:
Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create();
然后在你的业务字段上添加@Expose注解,但因为ObservableList的内部字段没有这个注解,Gson还是会序列化它的所有成员,所以这个方案实用性极低。
总之,优先选自定义TypeAdapter的方案,一次解决所有ObservableList的序列化问题,后续再也不用操心这个坑了。
内容的提问来源于stack exchange,提问作者Zephyr

