I'm trying to serialize my model inside my program. The model is named "ImageModel" and it implements Serializable. This model also contains another custom object named "Perspective" which is also implementing Serializable. I have a static utility class that serializes my model and reads it. This code has been tested in the main method with an "ImageModel" and everything is working perfectly.
But when I try to use it in the actual program I'm running into an issue. The "ImageModel" class is declared in my system class, named "MainWindow" which extends JFrame and is the link between most of the different classes. For some reason, I can't serialize the model doing something like MainWindow.getModel(). The compiler argues that my "EventFactory" is not serializable. This class is also declared in "MainWindow", but I'm not even understanding why Java wants to serialize it, I'm under the impression that java is not just trying to serialize the model, but also the GUI.
Here are segments of code :
My model:
public class ImageModel extends Observable implements Cloneable, Serializable {
private String path;
private ArrayList<Observer> observers;
private ArrayList<Perspective> perspectives;
private int numPerspectives;
private Perspective selectedPerspective;
...
}
The perspective class:
public class Perspective implements Serializable {
private ImageModel image;
private int degreeOfRotation;
private Point topLeftPoint;
private int zoomPercentage;
private int height;
private int width;
...
}
The actual GUI that declares the model and other elements:
public class MainWindow extends JFrame {
private final int GRID_ROWS = 0;
private final int GRID_COLUMNS = 2;
private final int NUM_PERSPECTIVE = 3;
private JPanel mainPane;
private ArrayList<View> perspectiveList;
private ImageModel imageModel;
private EventFactory eventFactory;
private JMenu menu;
private JToolBar toolBar;
...
}
The main method:
MainWindow mw = new MainWindow();
/*
* Does NOT work:
* ImageModel imageModel= mw.getImageModel();
* Utility.serializeModel(imageModel); //Crashes
*
* Works:
*
* ImageModel imageModel= new ImageModel();
* Utility.serializeModel(imageModel);
*
*/
Here are my two utility functions in case you need them :
public static void serializeModel(ImageModel imageModel)
{
String filename = "TEST.ser";
FileOutputStream fos = null;
ObjectOutputStream out = null;
try
{
fos = new FileOutputStream(filename);
out = new ObjectOutputStream(fos);
out.writeObject(imageModel);
out.close();
}
catch (IOException ex)
{
ex.printStackTrace();
}
}
public static ImageModel restoreModel(String filename)
{
ImageModel imageModel = null;
FileInputStream fis = null;
ObjectInputStream in = null;
try
{
fis = new FileInputStream(filename);
in = new ObjectInputStream(fis);
imageModel = (ImageModel)in.readObject();
in.close();
}
catch(IOException ex)
{
ex.printStackTrace();
}
catch(ClassNotFoundException ex)
{
ex.printStackTrace();
}
return imageModel;
}
Here's the STACK_TRACE of the error I'm receiving when working on the actual use case:
http://pastie.org/3008549
So yeah, like I'm saying, it's like if Java was trying to serialize other stuff around the model.
I'm guessing EventFactory is somehow making it's way into ImageModel's fields. Maybe indirectly linked from an Observer. Perhaps you should clear that list before attempting to serialise or set that field as transient.
Related
I had a class Inventory that contains a list of Treasure class. I designed the Reader to read the Inventory as a list of Treasure, but when I want to call the reader from my main function, it says that Inventory is not the same as a list of Treasure. And now I don't know what should I do, should I change the read method to only read Inventory, but then again Inventory contains all the list of Treasure that I need. I am very lost as how to do it.
public class Inventory implements SavedGames {
private ArrayList<Treasure> inventory;
private static Inventory instance;
// EFFECTS: constructs an empty player inventory
private Inventory() {
this.inventory = new ArrayList<Treasure>();
inventory.add(new Treasure("Potion", 4));
}
private void loadGames() {
try {
MainCharacter character = Reader.readCharacter(new File(CHARACTER_FILE));
Monster monster = Reader.readMonster(new File(MONSTER_FILE));
List<Treasure> inventory = Reader.readInventory(new File(INVENTORY_FILE));
this.character = character;
this.monster = monster;
this.inventory = inventory;
} catch (IOException e) {
System.out.println("Unable to read files, resetting...");
startApp();
}
}
private static List<Treasure> parseInventory(List<String> fileContent) {
List<Treasure> inventory = new ArrayList<>();
for (String line : fileContent) {
ArrayList<String> lineComponents = splitString(line);
inventory.add(parseTreasure(lineComponents));
}
return inventory;
}
private static Treasure parseTreasure(List<String> components) {
String description = components.get(0);
int amount = Integer.parseInt(components.get(1));
return new Treasure(description, amount);
}
}
Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object.
The byte stream created is platform independent. So, the object serialized on one platform can be deserialized on a different platform.
To make a Java object serializable we implement the java.io.Serializable interface.
The ObjectOutputStream class contains writeObject() method for serializing an Object.
public final void writeObject(Object obj)
throws IOException
The ObjectInputStream class contains readObject() method for deserializing an object.
public final Object readObject()
throws IOException,
ClassNotFoundException
I'm kind of stuck with a problem. I do understand the concept of serialization. Nevertheless I'm getting errors when I try to serialize/deserialize (deepCopy) an object:
I have a basic domain objects that hold information (two files):
public class DomainObject implements java.io.Serializable {
private String defaultDescription = "";
private List<Translation> translations;
public DomainObject() {
;
}
public void setTranslations(final List<Translation> newTranslations) {
this.translations = newTranslations;
}
public List<Translation> getTranslations() {
return this.translations;
}
public void setDefaultDescription(final String newDescription) {
this.defaultDescription = newDescription;
}
public String getDefaultDescription() {
return this.defaultDescription;
}
}
public class Translations implements java.io.Serializable {
private String description = "";
public Translation() {
;
}
public void setDescription(final String newDescription) {
this.description = newDescription;
}
public String getDescription() {
return this.description;
}
}
I also have a frame so the user can fill in all the necessary information for this domain object. Since I have multiple domain objects (this example only shows one) with different fields I have different frames for each domain object. Each of these frames includes a "MultiLanguageFrame" which gives the user the ability to add optional translations for this domain object's description.
public class MultiLanguageFrame extends org.eclipse.swt.widgets.Composite {
private List<Translation> translations = new ArrayList<Translation>();
public MultiLanguageFrame(final Composite parent, final int style) {
super(parent, style);
...
}
public List<Translation> getTranslations() {
return translations;
}
}
I deepCopy objects via this method:
...
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.flush();
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ois = new ObjectInputStream(bais);
return ois.readObject();
} catch (Exception t) {
logger.error(deepCopy() error: " + t.getMessage()); //$NON-NLS-1$
throw new RuntimeException("deepCopy() error", t); //$NON-NLS-1$
}
So now to the error:
When i try to do something like this:
MultiLanguageFrame frame = new MultiLanguageFrame(parent, SWT.NONE);
DomainObject dom = new DomainObject();
dom.setDefaultDescription("Testobject");
dom.setTranslations(frame.getTranslations())
deepCopy(dom);
I receive an error telling me that MultiLanguageFrame is not Serializable. Why would Java try to serialize the frame when I only want that DomainObject?
I thought maybe it is because of the reference in frame. So when I add the Serializable-Interface to MultiLanguageFrame and markt the SWT-Components as transient it tells me that no valid constructor was found. I can't add a parameterless constructor because it would logically make no sense and also SWT-Components need a parent to exist.
I'm really stuck with this problem because I do not know how to work around this. Thanks for answers in advance!
I found the solution myself. I'll just post this so others can see it, it might help.
Thanks to #greg-449 who lead the way. I do have an inner class TranslationHelper which extends Translation in MultiLanguageFrame. The purpose of this is so I can save some flags (deleted, changed, new) for Translations without changing Translation itself. When I call frame.getTranslations() I cast the elements from TranslationsHelper to Translation. The instance of the object remains a TranslationHelper though.
Now it all makes sense that MultiLanguageFrame was involved in all of this.
I am trying to create a form where the user saves three pieces of information (id, name and surname). The following is the code of the person:
public class Person implements Serializable {
private String Personfirstname;
private String Personlastname;
private String PersonID;
/**
* #return the Personfirstname
*/
public String getPersonfirstname() {
return Personfirstname;
}
/**
* #param Personfirstname the Personfirstname to set
*/
public void setPersonfirstname(String Personfirstname) {
this.Personfirstname = Personfirstname;
}
/**
* #return the Personlastname
*/
public String getPersonlastname() {
return Personlastname;
}
/**
* #param Personlastname the Personlastname to set
*/
public void setPersonlastname(String Personlastname) {
this.Personlastname = Personlastname;
}
/**
* #return the PersonID
*/
public String getPersonID() {
return PersonID;
}
/**
* #param PersonID the PersonID to set
*/
public void setPersonID(String PersonID) {
this.PersonID = PersonID;
}
public void savecons()
{
try {
File selectedFile = new File("Consultant - " + PersonID + ".txt");
FileOutputStream fileStream = new FileOutputStream(selectedFile);
ObjectOutputStream oos = new ObjectOutputStream(fileStream);
oos.writeObject(this);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
private String toString(int ConsultantID) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
public static Person loadcons() throws Exception
{
Person loadcons = null;
JFileChooser chooser = new JFileChooser();
int chooserOption = chooser.showSaveDialog(null);
chooserOption = JFileChooser.APPROVE_OPTION;
try {
File file = new File (chooser.getSelectedFile().getAbsolutePath());
ObjectInputStream input = new ObjectInputStream(new FileInputStream(file));
loadcons = (Person) input.readObject();
input.close();
return loadcons;
} catch (IOException ex) {
System.out.println(ex.getMessage());
} catch (ClassNotFoundException ex) {
System.out.println(ex.getMessage());
}
throw new Exception("No files were selected");
}
private String toString(String PersonID) {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
The code have the 3 variables and 2 methods. One of the methods saves the variable information into a text files (the text files is being outputted but I am not sure if the information is getting into it as its all symbols. The other method is a load button that will import the data back in the fields.
Then I created a form with the following code. There the saving is:
Person cons_save = new Person();
cons_save.setPersonfirstname(this.jTextField1.getText());
cons_save.setPersonlastname(this.jTextField2.getText());
cons_save.setPersonID(this.jTextField3.getText());
this.jTextField1.setText("");
this.jTextField2.setText("");
this.jTextField3.setText("");
cons_save.savecons();
and the loading is the below:
Person cons_load = Person.loadcons();
this.jTextField1.setText(cons_load.getPersonfirstname());
this.jTextField2.setText(cons_load.getPersonlastname());
this.jTextField3.setText(cons_load.getPersonID());
When I press the loading button it doesn't work as it needs an exception but when I create the exception the button works but when I chose the file, the information is not going to the fields.
Person cons_load;
try {
cons_load = Person.loadcons();
this.jTextField1.setText(cons_load.getPersonfirstname());
this.jTextField2.setText(cons_load.getPersonlastname());
this.jTextField3.setText(cons_load.getPersonID());
} catch (Exception ex) {
Logger.getLogger(CreateConsultant.class.getName()).log(Level.SEVERE, null, ex);
}
I appreciate every help I can get as this is the first time I am trying to program in java oop.
You will need to mark the classes you want to save to file with the Serializable interface. This should allow the serialization of the objects you are after.
As per the JavaDoc (I highlighted some text in bold):
Serializability of a class is enabled by the class implementing the
java.io.Serializable interface. Classes that do not implement this
interface will not have any of their state serialized or deserialized.
All subtypes of a serializable class are themselves serializable. The
serialization interface has no methods or fields and serves only to
identify the semantics of being serializable.
Essentially, this: public class Person needs to become this: public class Person implements Serializable { static final long serialVersionUID = ....
It is important that serialVersionUID is unique for each class since it is used for serialization and deserialization purposes.
EDIT: As per the comments below, I copied your code and ran it. I managed to save and read it back without issues. The code ran as is in your question, seeing that you have added the marker interface (it is good practice to also include your serialVersionUID field).
I then removed the implements Serializable section of your code, and I got this error: writing aborted; java.io.NotSerializableException: so.Person. This essentially shows that you are trying to store a non serializable item.
Below is what the content of the file looks like when the exception is thrown:
I was wondering if there was an easy way to save arrays of objects, without having to go through and save each aspect of the the objects. In my example I have two arrays, one a single array and the other a 2D array, that contain objects referring to a custom class. Each object has specific details like x and y ints, booleans, strings, ect. attached to them (block[0].x, block[0].canWalk, block[0].name) and I was wondering if there is an easy way of saving these arrays to a file without having to use a for loop and save each part. The multidimensional array is simply an array of saved arrays identical to the first one (savedBlock[0][0].x ...)
What I have so far (throwing NotSerializableException):
public class Save
{
static File f;
static ObjectOutputStream os;
public static void openFile()
{
try
{
if(!new File("c:\\IDsGame").exists())
{
new File("c:\\IDsGame").mkdirs();
}
f = new File("c:\\IDsGame\\data.bin");
os = new ObjectOutputStream(new FileOutputStream(f));
writeFile();
}
catch(Exception e)
{
System.err.println("creating file");
}
}
public static void writeFile()
{
try
{
ArrayList<Object> map = new ArrayList<Object>(Arrays.asList(Map.block));
ArrayList<Object> savedMaps = new ArrayList<Object>(Arrays.asList(Map.savedMaps));
os.writeObject(map);
os.writeObject(savedMaps);
os.close();
}
catch (IOException e) {System.out.println(e);}
}
}
Within my map class I initialize block (Blocks[]) and savedMaps(Blocks[][]). My Blocks class holds this:
public class Blocks implements Serializable
{
public boolean canWalk, onTop, itemTaken;
public Image img = null, imgBack = null;
public final Image (a ton of different images)
public String name, item, message, title;
public char initMap, initEx, initIt;
public int x, y, height, width;
public Blocks()
{
canWalk = true;
onTop = false;
itemTaken = false;
img = null;
name = null;
item = null;
message = null;
x = 0;
y = 0;
height = 0;
width = 0;
}
}
Obviously I change the certain parts different arrays within the Map class, and I was wondering if there was any easier way (at all) to save the arrays of Blocks Objects.
Thanks for taking your time to help and if you need any more specific just let me know.
I.D.
Image is not serializable, so you receive a NotSerializableException when the Blocks class is serialized. ImageIcon can be serialized, so wrapping Image instances in ImageIcons will solve that issue.
public class Blocks implements Serializable
{
public boolean canWalk, onTop, itemTaken;
public ImageIcon img = null, imgBack = null;
public final ImageIcon (a ton of different images)
public String name, item, message, title;
public char initMap, initEx, initIt;
public int x, y, height, width;
public Blocks()
{
canWalk = true;
onTop = false;
itemTaken = false;
img = null;
// img = new ImageIcon(someImageInstance)
name = null;
item = null;
message = null;
x = 0;
y = 0;
height = 0;
width = 0;
}
}
Just making a class implement Serializable is not enough: All the fields must be Serializable too.
Your Block class may have a problem. All the usual java classes are Serializable, but Block also has fields of type Image. If Image isn't Serializable, then attempting to serialize Block will throw NotSerializableException.
I have been searching here and found answers to this problem but can't seem to make them work for me. Basicly I have a GUI that contains a JTree inside a JScrollPane:
private void initComponents() {
scroll = new javax.swing.JScrollPane();
nodo padre = new nodo();
modeloArbol modelo = new modeloArbol(padre);
arbol = new arbolNodos(modelo);
I have the following classes:
public class arbolNodos extends JTree implements Serializable{
public arbolNodos(TreeModel newModel) {
public class listenerModeloArbol implements TreeModelListener{
//Overriding treeNodes*(TreeModelEvent e)
public class modeloArbol extends DefaultTreeModel implements Serializable{
public modeloArbol(TreeNode root) {
super(root);
}
public class nodo extends DefaultMutableTreeNode implements Serializable{
I also have 2 buttons that store (botonGuardarArbol) and retrieve (botonCargarArbol) using the XMLEncoder the JTree (they really retrieve the root node of the tree) into a file with the corresponding actionPerformed listeners:
private void botonGuardarArbolActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try {
XMLEncoder encoder = new XMLEncoder(new BufferedOutputStream(new FileOutputStream("C:\\borrar\\presupuesto")));
encoder.writeObject(arbol.getModel().getRoot());
encoder.close();
private void botonCargarArbolActionPerformed(java.awt.event.ActionEvent evt) {
// TODO add your handling code here:
try {
XMLDecoder decoder = new XMLDecoder( new BufferedInputStream(new FileInputStream("C:\\borrar\\presupuesto")));
nodo padre = (nodo) decoder.readObject();
modeloArbol modelo = new modeloArbol(padre);
decoder.close();
modelo.setRoot(padre);
arbolNodos arbolNuevo = null;
if( modelo != null ){
arbolNuevo = new arbolNodos(modelo);
arbol = null;
arbol = arbolNuevo;
((DefaultTreeModel)arbol.getModel()).reload();
}
else
arbolNuevo = new arbolNodos();
The thing is, when I retrieve the stored JTree, I know it is well retrieved because I try printing attributes inside all the nodes of the Tree and they are ok, but when I try putting it up in the Swing it doesn't refresh and the view bloks (ie. I can't manipulate anything in the JTree I had before doing loading). I have tried the numerous threads of refresh/update JTree but couldn't fix it. How can I do this?
Thank you
if( modelo != null )
{
arbolNuevo = new arbolNodos(modelo); //
arbol = null;
arbol = arbolNuevo;
((DefaultTreeModel)arbol.getModel()).reload();
}
else
arbolNuevo = new arbolNodos();
Don't keep creating new JTree objects. If you want to change the model then just use:
tree.setModel(...);
Also, why are you extend JTree, DefaultTreeModel, DefaultMutableTreeNode? If for some reason you do need to extend those classes then follow standard Java naming conventions for those classes. Classes start with an upper case character.