【问题标题】:NotSerializableException on SimpleListPropertySimpleListProperty 上的 NotSerializableException
【发布时间】:2013-09-18 10:49:54
【问题描述】:

我正在使用 Javafx,我将我的对象包装到 ListProperty 中,以让 tableview 更新列表对象的任何更改。现在我正在尝试序列化我的项目和对象的 ListProperty,它抛出了这个异常。

java.io.NotSerializableException: javafx.beans.property.SimpleListProperty
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1181)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1541)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1506)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1429)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1175)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
at Util.FileManager.serializeProject(FileManager.java:23)
at Controller.FrameworkController.saveProject(FrameworkController.java:549)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:75)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:279)
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(FXMLLoader.java:1435)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:28)
at javafx.event.Event.fireEvent(Event.java:171)
at javafx.scene.control.MenuItem.fire(MenuItem.java:456)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer.doSelect(ContextMenuContent.java:1197)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1148)
at com.sun.javafx.scene.control.skin.ContextMenuContent$MenuItemContainer$6.handle(ContextMenuContent.java:1146)
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:69)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:217)
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:170)
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:38)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:37)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:35)
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:92)
at com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:53)
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:33)
at javafx.event.Event.fireEvent(Event.java:171)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3328)
at javafx.scene.Scene$MouseHandler.process(Scene.java:3168)
at javafx.scene.Scene$MouseHandler.access$1900(Scene.java:3123)
at javafx.scene.Scene.impl_processMouseEvent(Scene.java:1563)
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Scene.java:2265)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:250)
at com.sun.javafx.tk.quantum.GlassViewEventHandler$MouseEventNotification.run(GlassViewEventHandler.java:173)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(GlassViewEventHandler.java:292)
at com.sun.glass.ui.View.handleMouseEvent(View.java:528)
at com.sun.glass.ui.View.notifyMouse(View.java:922)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
at java.lang.Thread.run(Thread.java:724)

我的项目类是这样的,我自己的所有对象都已经实现了可序列化。

public class Project implements Serializable{

private String name;
private String standard;
private ListProperty<Equipment> projectEquipments;

private ListProperty<LegendElement> equipmentsLegend;

public Project() {
    this.projectEquipments = new SimpleListProperty<Equipment>(FXCollections.observableArrayList(new ArrayList<Equipment>()));

    this.equipmentsLegend = new SimpleListProperty<>(FXCollections.observableList(new ArrayList<LegendElement>()));}

我可以做些什么来序列化我的项目和其中的设备列表?

【问题讨论】:

    标签: javafx notserializableexception listproperty


    【解决方案1】:

    您需要为您的项目创建一个自定义序列化。

    详情请参考以下文章:

    1. StackOverflow question on Custom Serialization in Java
    2. Official Oracle Tutorial on Custom Serialization in Java

    或者,您可以使用诸如JAXBjavax.json 之类的技术使用诸如JSON 或XML 之类的文本格式编写序列化。

    所有 JavaFX 项目(以及从 JavaFX 2.x 开始的 JavaFX 框架中的几乎所有对象)都没有实现 Serializable,因此您不能直接序列化 JavaFX 属性。

    【讨论】:

    • 所以我必须在每个对象中使用这些方法(写入和读取)来写入和读取其不可序列化的每个成员?例如,因为每个设备对象都有其他成员,例如 SimpleStringProperty ,SimpleIntegerProperty,甚至其他设备对象(在这种情况下,我只想引用已经存在的设备,而不是再次写入/读取)。
    • “所有 JavaFX 项目(以及 JavaFX 2.x 起几乎所有 JavaFX 框架中的对象)都没有实现 Serializable,因此您不能直接序列化 JavaFX 属性。” - 这是一种耻辱,并且会导致复杂对象图的大量工作,
    【解决方案2】:

    我遇到了同样的问题:我的解决方案。

    正在使用接口 Externalizable 并编写我自己的 writeExternal 和 readExternal 方法。

    后来我使用 XStream 时遇到了 Externalizable 的问题。快速修复是使用下面的代码更改 ExternalizableConverter 的优先级。

        xstream.registerConverter(new ReflectionConverter(xstream.getMapper(),
                xstream.getReflectionProvider()) {
            public boolean canConvert(Class type) {
                return Externalizable.class.isAssignableFrom(type);
            }
        }, XStream.PRIORITY_LOW);
    

    【讨论】:

      【解决方案3】:

      为避免 NotSerializableException,请确保:

      1. 你的类实现了可序列化
      2. 所有非原始成员都实现了可序列化(或者是临时的)
      3. 如果您的类是内部类,它要么是静态的,要么是外部类实现的 可序列化

      此外,您还需要为每个 Serializable 类定义 serialVersionUID。检查以上所有 3 个案例加:

      1. 所有可序列化的超类
      2. 如果你的类是匿名类,也可以在那里定义

      注意:您的代码有时可能会在没有 serialVersionUID 的情况下运行,但 read the last paragraph in Serializable's javadoc 了解为什么会这样

      视环境而定。


      有一个 VM 选项可以向异常添加详细信息。它将显示无法序列化的根类和嵌套类,并帮助您找出缺少的内容:

      -Dsun.io.serialization.extendedDebugInfo=true
      

      【讨论】:

        【解决方案4】:

        这是适合我的解决方案(在 JPA 实体上序列化 SimpleXXXProperty,其中 xxx 可以是字符串、对象等)

        https://gist.github.com/james-d/a7202039b00170256293

        你“只是”必须:
        1) 实现可序列化
        2) 将所有属性设置为瞬态
        3)定义2个这样的特殊方法:

        private void writeObject(ObjectOutputStream s) throws IOException {
            s.defaultWriteObject();
            s.writeLong(idProperty().longValue());
            s.writeUTF(aStringProperty().getValueSafe()); // can't be null so use getValueSafe that returns empty string if it's null
            s.writeUTF(anOtherStringPoperty().getValueSafe());
        }
        

        private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            idProperty.set(s.readLong());
            aStringProperty.set(s.readUTF());
            anOtherStringPoperty(s.readUTF());
            // set values in the same order as writeObject()
        }
        

        瞧 :) 你可以用

        序列化你的对象
        MyObject o = new MyObject();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(o);
        oos.close();
        

        请注意,这些奇怪的私有方法是由Java虚拟机直接调用的,不能像官方文档中提到的那样被Override、public或其他。

        已编辑:不要对 String 使用 writeChars,而是使用 writeUTF。
        请记住,在反序列化过程中,“读取的对象”没有被实例化(没有调用构造函数!),然后所有属性都没有初始化,因此 idProperty.set(o) 将抛出 nullPointerException。
        所以你必须创建一个 initMethode 来初始化所有属性。在从 ObjectInputStream 读取数据之前,从您的构造函数和 readObject 方法调用此方法

        编辑:我为自动读写做了助手。如果你想使用它,这里是代码:

        public class WriteObjectsHelper {
        
        // write a StringProperty to ObjectOutputStream
        public static void writeStringProp(ObjectOutputStream s, StringProperty strProp) throws IOException {
            s.writeUTF(strProp.getValueSafe());
        }
        
        // write a ListProperty to ObjectOutputStream
        public static void writeListProp(ObjectOutputStream s, ListProperty lstProp) throws IOException {
            if(lstProp==null || lstProp.getValue()==null) {
                s.writeInt(0);
                return;
            }
            s.writeInt(lstProp.size());
            for(Object elt:lstProp.getValue()) s.writeObject(elt);
        }
        
        // automatic write set of properties to ObjectOutputStream
        public static void writeAllProp(ObjectOutputStream s, Property... properties) throws IOException {
            s.defaultWriteObject();
            for(Property prop:properties) {
                if(prop instanceof IntegerProperty) s.writeInt(((IntegerProperty) prop).intValue());
                else if(prop instanceof LongProperty) s.writeLong(((LongProperty) prop).longValue());
                else if(prop instanceof StringProperty) s.writeUTF(((StringProperty)prop).getValueSafe());
                else if(prop instanceof BooleanProperty) s.writeBoolean(((BooleanProperty)prop).get());
                else if(prop instanceof ListProperty) writeListProp(s,(ListProperty)prop);
                else if(prop instanceof ObjectProperty) s.writeObject(((ObjectProperty) prop).get());
                else throw new RuntimeException("Type d'objet incompatible : " + prop.toString());
            }
        }
        }
        
        public class ReadObjectsHelper {
        
        // Read a ListProperty from ObjectInputStream (and return it)
        public static ListProperty readListProp(ObjectInputStream s) throws IOException, ClassNotFoundException {
            ListProperty lst=new SimpleListProperty(FXCollections.observableArrayList());
            int loop=s.readInt();
            for(int i = 0;i<loop;i++) {
                lst.add(s.readObject());
            }
        
            return lst;
        }
        
        // automatic fill a set of properties with values contained in ObjectInputStream
        public static void readAllProp(ObjectInputStream s, Property... properties) throws IOException, ClassNotFoundException {
            for(Property prop:properties) {
                if(prop instanceof IntegerProperty) ((IntegerProperty)prop).setValue(s.readInt());
                else if(prop instanceof LongProperty) ((LongProperty)prop).setValue(s.readLong());
                else if(prop instanceof StringProperty) ((StringProperty)prop).setValue(s.readUTF());
                else if(prop instanceof BooleanProperty) ((BooleanProperty)prop).setValue(s.readBoolean());
                else if(prop instanceof ListProperty) ((ListProperty)prop).setValue(readListProp(s));
                else if(prop instanceof ObjectProperty) ((ObjectProperty)prop).setValue(s.readObject());
                else throw new RuntimeException("Unsupported object type : " + prop==null?null:prop.toString());
            }
        }
        }
        

        还有他们,这是我的(法语)JPA 实体的一部分:

        @Entity
        @Table(name="articles")
        public class Article implements Serializable, IEntity {
        
        private transient LongProperty idArticle;
        public LongProperty idArticleProperty() { return idArticle; }
        
        private transient StringProperty descriptionFr;
        public StringProperty descriptionFrProperty() { return descriptionFr; }
        
        private transient StringProperty reference;
        public StringProperty referenceProperty() { return reference; }
        
        private transient StringProperty constructeur;
        public StringProperty constructeurProperty() { return constructeur; }
        
        private transient StringProperty numSAP;
        public StringProperty numSAPProperty() { return numSAP; }
        
        private transient StringProperty descriptionEn;
        public StringProperty descriptionEnProperty() { return descriptionEn; }
        
        private transient ListProperty<Article> testList;
        public ListProperty<Article> articlesProperty() {return testList; }
        
        public Article() {
            initInstance();
        }
        
        /**
         * Need for calling by readObject;
         */
        private void initInstance() {
            idArticle=new SimpleLongProperty();
            descriptionFr=new SimpleStringProperty();
            descriptionEn=new SimpleStringProperty();
            reference=new SimpleStringProperty();
            constructeur=new SimpleStringProperty();
            numSAP=new SimpleStringProperty();
            testList=new SimpleListProperty<>(FXCollections.observableArrayList());
        }
        
        private void writeObject(ObjectOutputStream s) throws IOException {
            WriteObjectsHelper.writeAllProp(s,idArticle,descriptionFr,reference,constructeur,numSAP,descriptionEn,testList);
        }
        
        private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            initInstance();
            ReadObjectsHelper.readAllProp(s, idArticle,descriptionFr,reference,constructeur,numSAP,descriptionEn,testList);
        }
        
        @Column(name = "idArticle")
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        public Long getIdArticle() {
            return idArticle.get();
        }
        
        public void setIdArticle(Long idArticle) {
            this.idArticle.set(idArticle);
        }
        //...
        }
        

        【讨论】:

          【解决方案5】:

          尝试创建一个演示类,作为前端和后端之间的桥梁。使用可序列化对主类文件执行后端操作,但在创建的演示类的帮助下访问它,数据类型为 SimplexxxProperty。在演示类的构造函数中执行转换操作 要么 尝试从类中删除 tostring()。

          【讨论】:

          • 您可以通过发布示例代码来提高此答案的质量。
          【解决方案6】:

          属性Object需要实现Serializable接口。这是SimpleObjectProperty 的一个简单扩展,用于可序列化的通用对象:

          public class WriteableObjectProperty<T> extends SimpleObjectProperty<T> implements Serializable {
          
              public WriteableObjectProperty () {
                  super();
              }
          
              public WriteableObjectProperty (T obj) {
                  super(obj);
              }
          
          
              private void writeObject (ObjectOutputStream s) throws IOException {
                  s.defaultWriteObject();
                  s.writeObject(get());
              }
          
              private void readObject (ObjectInputStream s) throws IOException, ClassNotFoundException {
                  s.defaultReadObject();
                  set((T) s.readObject());
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2023-03-29
            • 2015-06-01
            • 2013-05-14
            • 2023-04-03
            • 2014-07-23
            • 2015-07-03
            相关资源
            最近更新 更多