I'm new serialization (and programming in general) so i don't really know what happens under the hood. I'm making an email client program and the class
FileHandlerObject is used to write and read email messages that i send. I maintain an ArrayList and it is read, updated with new email and then written again. I don't understand why it throws an InvalidClassException because the file contains an ArrayList so the casting should be no problem.
I'm maintaining an ArrayList because the writeObject() truncates the file every time it writes.(Let me know if im wrong here)
class FileHandlerObject implements MyFileHandler<EmailMessage>{
public void write(EmailMessage input){
try
{
FileInputStream fileInputStream=new FileInputStream("emails.ser");
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
#SuppressWarnings("unchecked")
ArrayList<EmailMessage> messagelist= (ArrayList<EmailMessage>) objectInputStream.readObject();
objectInputStream.close();
messagelist.add(input);
FileOutputStream fileOutputStream = new FileOutputStream("emails.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(messagelist);
objectOutputStream.flush();
objectOutputStream.close();
}
catch(FileNotFoundException f)
{
try{
FileOutputStream fileOutputStream = new FileOutputStream("emails.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
ArrayList<EmailMessage> messagelist=new ArrayList<EmailMessage>();
messagelist.add(input);
objectOutputStream.writeObject(messagelist);
objectOutputStream.flush();
objectOutputStream.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
catch(IOException e){
e.printStackTrace();
}
catch(ClassNotFoundException e)
{
e.printStackTrace();
}
}
public ArrayList<EmailMessage> read(){
try{
FileInputStream fileInputStream=new FileInputStream("emails.ser");
ObjectInputStream objectInputStream=new ObjectInputStream(fileInputStream);
#SuppressWarnings("unchecked")
ArrayList<EmailMessage> messagelist= (ArrayList<EmailMessage>) objectInputStream.readObject();
objectInputStream.close();
return messagelist;
}
catch(IOException e)
{
e.printStackTrace();
}
catch(ClassNotFoundException c)
{
c.printStackTrace();
}
catch(ClassCastException c)
{
c.printStackTrace();
}
return null;
}
As the docs explain, this means one of a few things. The code you pasted does not give any insights about which of these mistakes you've made.
You saved some emails. Then, you edited the EmailMessage class, for example by adding a method or changing the name of a field, and then you ran the code again which ends up reading that file that was 'saved' with an old version of EmailMessage and which you are now trying to read with a new version. The java serialization mechanism can theoretically deal with versioning schemes but in practice it's a disaster. Ditch serialization, write your own protocol instead.
The EmailMessage class does not implement Serializable and does not have a no-args constructor. Give it a no-args constructor, and add implements Serializable to it.
Your classpath is incomplete; EmailMessage contains fields or methods that refer to some other class you wrote and it is not available on the classpath right now. Fix your classpath.
Related
I have created a TableView that allows the user to enter information into the table. The issue I am having is that I need to save the information the user provided, and reload the same table, with the same information the next time the program is ran. I've been stuck for some time on this, if anyone can help I would greatly appreciate it. BELOW is my ObservableList and the methods I tried. I will include a screen capture of what the table looks like. I called the methods from button pushes the user would make.
//get Trail info
public static ObservableList<Stats> getTrailInfo(){
ObservableList<Stats> trailInfo = FXCollections.observableArrayList();
trailInfo.add(new Stats(trailList, timesHiked, daysHiked, hoursHiked, minutesHiked, milesHiked));
return trailInfo;
}
//I ATTEMPTED TO SAVE AND READ IT BACK USING THESE METHODS, BUT I DID NOT HAVE ANY LUCK
private static void write(ObservableList<Stats> stats) {
try {
// write object to file
OutputStream out = new FileOutputStream("TrailName.txt");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(new ArrayList<Stats>(stats));
oos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
static ObservableList<Stats> pull() {
try {
InputStream inputstream = new FileInputStream("TrailName.txt");
ObjectInputStream ois = new ObjectInputStream(inputstream);
List<Stats> list = (List<Stats>) ois.readObject() ;
return FXCollections.observableList(list);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return FXCollections.emptyObservableList();
}
[TableView ScreenShot][1]
[1]: https://i.stack.imgur.com/m0nyg.png
Does Stats implement java.io.Serializable? From the API docs from ObjectOutputStream:
Only objects that support the java.io.Serializable interface can be written to streams.
From the docs on Serializable:
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.
Appended are my little utility functions for serialising objects. I just encountered following problem:
I renamed a package and suddenly I get a java.lang.ClassCastException when opening my app and trying to read serialised data...
Can I somehow solve that? I would like my serialisations to be working after a renaming, can I do something to implement this? Via some versioning for example?
Here are my two simple functions I use currently:
public static String serialize(Object object)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try
{
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(object);
oos.flush();
oos.close();
} catch (IOException e)
{
L.e(StringUtil.class, e);
}
return Base64.encodeToString(baos.toByteArray(), 0);
}
public static <T> T deserialize(String serializedObject, Class<T> clazz)
{
if (serializedObject == null)
return (T)null;
byte [] data = Base64.decode(serializedObject, 0);
Object o = null;
try
{
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
o = ois.readObject();
ois.close();
}
catch (IOException e)
{
L.e(StringUtil.class, e);
}
catch (ClassNotFoundException e) {
L.e(StringUtil.class, e);
}
return (T)o;
}
I can suggest next options:
add support to your deserialize method to deal with old package names
convert byte [] data to String
replace old package name with new in deserialized data (with regexp)
continue to deserialize with ObjectInputStream
public class Inventory implements Serializable {
ArrayList<Product> productlist;
File file;
public Inventory(){
productlist = new ArrayList<Product>();
file = new File("build/classes/inventory/inv.ser");
if(!file.exists()){
try {
file.getParentFile().mkdirs();
file.createNewFile();
} catch (IOException ex) {
Logger.getLogger(Inventory.class.getName()).log(Level.SEVERE, null, ex);
}
}
if(file.length() !=0){
loadFile(file);
}
}
public void addProduct(Product product){
productlist.add(product);
saveFile(this.file);
}
public void saveFile(File file){
try{
FileOutputStream fos = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(productlist);
out.close();
fos.close();
} catch(FileNotFoundException ex){System.out.println("FileNotFoundException");}
catch(IOException ex){System.out.println("InputException");}
}
public void loadFile(File file){
try{
FileInputStream fis = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fis);
productlist=(ArrayList<Product>)in.readObject();
in.close();
fis.close();
} catch(FileNotFoundException ex){ System.out.println("FileNotFoundException"); }
catch(IOException ex){System.out.println("OutputException");}
catch(ClassNotFoundException ex){System.out.println("ClassNotFoundException");}
}
}
Does writeObject() overwrite the content of the existing file or append the objects to the existing file?
And is it a good idea to serialize an ArrayList of Objects like what i did inside the saveFile method?
Does writeObject() overwrite the content of the existing file or append the objects to the existing file?
Neither. It writes the object to the underlying stream. The underlying stream is a serial byte stream that can only be appended to. In this case the underlying stream is backed by an underlying file, which has or has not already been overwritten, depending on how you constructed the FileOutputStream. It has nothing to do with writeObject(). In any case you can't successfully append to a file of serialized objects without taking special measures.
And is it a good idea to serialize an ArrayList of Objects like what i did inside the saveFile method?
Compared to what?
N.B.
When you get an exception, print it. Not just some message of your own devising.
Creating a file just so you can test it for zero length doesn't make sense.
The directory build/classes/inventory won't be there at runtime once you stop using the IDE. This is no place to put a file.
You could try FileUtils function to write list of object into plane text file.
Please find below URL for reference -
http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/FileUtils.html#writeLines-java.io.File-java.util.Collection-java.lang.String-boolean-
e.g.
FileUtils.writeLines(new File(fileToAttach), list);
I have an ArrayList filled with some custom POJO's that I'd like to persist when the user switches screen orientation (onSaveInstanceState) and when e.g. the user hits the back button (onPause).
As far as I know, the SharedPreferences can only hold primitive data types and bundles can not hold references to generic ArrayLists.
Any tips on how to tackle this?
Regards,
Marcus
1- create a class and put everything you want to store for example arraylist of your POJO and make that class implement Serializable interface.
class MyBundle implements Serializable {
ArrayList<POJO> mPOJO;
MyBundle( ArrayList<POJO> pojo){
mPOJO= pojo;
}
}
2- write it to file:
ObjectOutputStream oos = null;
FileOutputStream fout = null;
try{
FileOutputStream fout = new FileOutputStream("Your file path");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(mb); // mb is an instance of MyBundle
} catch (Exception ex) {
e.printStackTrace();
}finally {
if(oos != null){
oos.close();
}
}
and to get back everything:
ObjectInputStream objectinputstream = null;
try {
streamIn = new FileInputStream("Your file address");
objectinputstream = new ObjectInputStream(streamIn);
MyBundle mb = (MyBundle) objectinputstream.readObject();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(objectinputstream != null){
objectinputstream .close();
}
}
i don't know this is correct method or not but i handle this like this this always success while your app lost all cache data itself then also u can get back serializable object->
for generic ArrayLists always use serializable
just look at once http://developer.android.com/reference/java/io/Serializable.html
Try converting the List to Json using Gson or Jackson.
Store the string in the Shared preference. some thing like below code
String listString = gsonSD.toJson(list<object> instance);
SharedPreferences storeDataPref = getContext().getSharedPreferences("list_store_pref", Context.MODE_PRIVATE);
Editor storeDataEditor = storeDataPref.edit();
storeDataEditor.putString("liststringdata", listString).apply();
I'm trying to read from a file I have saved using the following code:
public void saveOnFile() {
try {
ObjectOutputStream output = new ObjectOutputStream(new FileOutputStream("object.txt"));
output.writeObject(mediaList);
output.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
public void readFromFile() {
try {
ObjectInputStream inStream = new ObjectInputStream(new FileInputStream("object.txt"));
this.mediaList.add( (Media) inStream.readObject());
inStream.close();
} catch(Exception ex) {
ex.printStackTrace();
}
}
However, I don't know if ObjectInputStream knows what objects are inputted. Objects in this case can either be a "Movie" or a "TVShow" but the are all subclasses to Media.
I get the following errors:
java.lang.ClassCastException: java.util.ArrayList cannot be cast to project.Media
at project.MediaHandler.readFromFile(MediaHandler.java:66)
at project.Window.<init>(Window.java:73)
at project.Window.main(Window.java:199)
The Object that you wrote to the ObjectOutputStream is called mediaList, therefore I assume that this is an ArrayList<Media> rather than each individual Media object.
On the ObjectInputStream, you are reading an object and casting it to Media, but I think that you will find that it is an ArrayList<Media> (or however mediaList is defined when it is written to the ObjectOutputStream).