For anyone who might find this post in a search who is trying to do the same thing, here is a quick how-to.
In Conclusion:
ObjectOutputStream can still be used to save to files, it just needs to writes Externalizable’s instead of Serializable’s. A Serializable will automatically write all the data to a file, but it will make it impossible to use a file that was saved after modification of a class. An Externalizable gives you direct control over what is saved to a file, therefore enabling default values for new fields or ignoring deleted ones. Furthermore, you can make smaller save files by specifying exactly what needs to be saved and ignoring what doesn’t.
I made a class called Writable that became the superclass for any class that is saved to a file. Writable implements Externalizable, and is provided below.
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.HashMap;
/**
* @author Eli Delventhal
*
* This class is the superclass of anything that will ever need to be written to a file.
* This includes practically everything in the game. The class includes all the methods needed
* to write to a file as an Externalizable, and provides the means for subclasses to specify
* exactly what is written and read to and from the files.
*/
public abstract class Writable implements java.io.Externalizable
{
//Set the version ID as something constatnt so it can always read the file
//This variable can be absolutely anything you want, as long as it remains constant
private static final long serialVersionUID = 3487495895819393L;
//Reads from a file, required by Externalizable
public void readExternal(ObjectInput input)
{
try
{
loadFile((HashMap) input.readObject());
}
catch (Exception e) {System.out.println("Error reading " + this + "! " + e);}
}
//Writes to a file, required from Externalizable
public void writeExternal(ObjectOutput out)
{
try
{
out.writeObject(setFile());
}
catch (Exception e) {System.out.println("Error writing " + this + "! " + e);}
}
//A sublass that is loaded from a file must override this method
//in order to specify exactly what is loaded from the file
public void loadFile(HashMap map)
{
//implemented by subclasses
}
//A sublass that is saved to a file must override this method
//in order to specify exactly what is saved to the file
public HashMap setFile()
{
return new HashMap();
}
}
Then, within each sublass of Writable (whatever is written to the file), override the last two methods in order to specify what is saved and loaded. Make sure to call the superclass version of the method as well, in case you have a class hierarchy. An example is provided below.
//Sets the values inside the passed HashMap to the appropriate members
public void loadFile(HashMap map)
{
String nameVar = (String) map.get("name");
String imageFolderVar = (String) map.get("imageFolder");
Integer valueVar = (Integer) map.get("value");
Integer weightVar = (Integer) map.get("weight");
if (nameVar != null)
name = nameVar;
if (imageFolderVar != null)
imageFolder = imageFolderVar;
if (valueVar != null)
value = valueVar.intValue();
if (weightVar != null)
weight = weightVar.intValue();
super.loadFile(map);
}
//Creates a HashMap to be saved to a file
public HashMap setFile()
{
HashMap map = new HashMap();
map.put("name",name);
map.put("imageFolder",imageFolder);
map.put("value",new Integer(value));
map.put("weight",new Integer(weight));
map.putAll(super.setFile());
return map;
}
Make sure to set the SerialVersionUID in each and every class that is written to the file. Annoying, yes. Neccessary, yes. Also, each and every saved class must have a default no argument constructor. This is what Externalizable calls before it calls your loadFile method. You can use this to create any default values you might need. Say, for example, you add in a Color field and want any older versions of the class loaded to start with Color.RED, while normally within your coding the class is constructed with a Color parameter, you simply set this in the default constructor. The purpose of the “if (so-and-so != null)” is for if an older version of the class never wrote that to the file. You want to use what was already set in your default constructor rather than setting it to the null that the HashMap will return.
And that’s pretty much it. Go ahead and use any of that code all you want, I hope this thread and this post helped anyone who might stumble on this thread.
Also, a big hats off to Jeff, c_lilian, and kul_th_las. Thanks a lot for the help and prodding me through this, guys.