I\'m using Javafx, and I wrap my objects into ListProperty to let the tableview updates for any changes on the objects of the list. Now I\'m trying to serialize my project a
I had the same Problem: my solution.
was using the interface Externalizable and write my own writeExternal and readExternal Method.
Later i've used XStream an had a problem with Externalizable. The quick fix was changing the priority of ExternalizableConverter with the code below.
xstream.registerConverter(new ReflectionConverter(xstream.getMapper(),
xstream.getReflectionProvider()) {
public boolean canConvert(Class type) {
return Externalizable.class.isAssignableFrom(type);
}
}, XStream.PRIORITY_LOW);
Here is the solution that works for me (Serialize SimpleXXXProperty on a JPA Entity where xxx can be String, Object, etc)
https://gist.github.com/james-d/a7202039b00170256293
You 'just' have to :
1) implements Serializable
2) set all Properties as transient
3) define 2 specials methodes like that :
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());
}
and
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()
}
and voilà :) You can serialize your Object with
MyObject o = new MyObject();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(o);
oos.close();
Note that these strange private methodes are directly called by the Java Virtual Machine and must not be Override, public or other as mentionned in the official documentation.
Edited : don't use writeChars for String but writeUTF.
And remember that on deserialization process, the "readed object" isn't instanciated (constructor isn't called!) and then all Properties aren't initialized and so idProperty.set(o) will throws nullPointerException.
So you have to create a initMethode where all properties are initialized. Call this methode from your constructor AND from readObject methode before read data from the ObjectInputStream
Edit : I made helpers for automatic write and read. Here is code if you want to use it :
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());
}
}
}
And them, here is part of my (french) JPA entity :
@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);
}
//...
}
Try creating a demo class that acts as a bridge between the front end and back end. Perform the backend operations on the main class file using serializable but access that with the help of the created demo class with the data type as SimplexxxProperty. Perform the conversion operation in the constructor of the demo class OR Try removing tostring() from the class.
To avoid NotSerializableException make sure:
Besides that you also need to define serialVersionUID for every Serializable class. Check all 3 cases above plus:
Note: your code may run without serialVersionUID sometimes but read the last paragraph in Serializable's javadoc to understand why it will
be a problem depending on the environment.
There's a VM option to add details to the exception. It will show the root and nested classes failing to serialize and help you figure out what you're missing:
-Dsun.io.serialization.extendedDebugInfo=true
The property Object
needs to implement the Serializable
interface. Here is a simple extension of SimpleObjectProperty
, for generic objects, that is serializable:
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());
}
}
You will need to create a custom serialization for your project.
Refer to the following articles for details:
Alternately, you could write serialize using a text format such as JSON or XML using technology such as JAXB or javax.json.
All JavaFX projects (and pretty much every object in the JavaFX framework as of JavaFX 2.x), does not implement Serializable so you cannot directly serialize a JavaFX property.