I have an Object called Doodle, I serialize it into a String and it's ok.
The problem arrises when I try to deserialize the object, the error is this:
java.io.InvalidClassException: java.util.ArrayList; local class incompatible: stream classdesc serialVersionUID = 8664875232659988799, local class serialVersionUID = 8683452581122892189
The methods to serialize and deserialize are the following:
public static String serDoodle(Doodle dood){
String serializzato = null;
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream so = new ObjectOutputStream(bo);
so.writeObject(dood);
so.flush();
serializzato = bo.toString();
so.close();
bo.close();
} catch (Exception e) {
System.out.println(e);
}
return serializzato;
}
public static Doodle deserDoodle(String deserializza){
Doodle dod = new Doodle();
try {
byte[] b = deserializza.getBytes();
ByteArrayInputStream bi = new ByteArrayInputStream(b);
ObjectInputStream si = new ObjectInputStream(bi);
dod=(Doodle) si.readObject();
si.readObject().getClass();
si.close();
bi.close();
} catch (Exception e) {
System.out.println("deserDoodle "+e);
}
return dod;
}
I use the same method(but with different variable) to serialize another type of object and with that one it works greatly.
I don't understand where is the trouble!
I serialize it into a String and it's ok
No, it isn't OK. String is not a container for binary data. The round-trip between byte-array and String isn't guaranteed to be losses. Don't do this. Use byte[], or at least Base64-encode it.
Related
This question already has answers here:
Java serialization, UID not changed. Can I add new variables and method to the class?
(4 answers)
Closed 2 years ago.
I have a class with name and age
class Person implements serializable {
int age;
String name;
}
I have an ArrayList of this class
ArrayList<Person> persons = new ArrayList<Person>
When tomcat shuts down, I save the above to a local file:
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = null;
try {
out = new ObjectOutputStream(bos);
out.writeObject(persons);
out.flush();
byte[] objBytes = bos.toByteArray();
File file = new File("./persons.bin");
Files.write(objBytes, file);
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
try {
bos.close();
} catch (IOException ex) {
}
}
and when tomcat starts up, I load the file back:
File file = new File("./persons.bin");
if (!file.exists()) return;
byte[] objBytes = Files.toByteArray(file);
ByteArrayInputStream bis = new ByteArrayInputStream(objBytes);
ObjectInput in = null;
try {
in = new ObjectInputStream(bis);
ArrayList<Person> persons = (ArrayList<Person>) in.readObject();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException ex) {
}
}
The above works as expected. My question is what will happen if later on the Person class will be added with new properties, for example:
class Person implements serializable {
int age;
String name;
String address;
}
I'm not using Gson because of the overhead it creates and I prefer to save the data as binary. Will the above fail? Succeed but address will be null? Weird things will happen? Do I have any control over what can be done in such cases?
Thanks
Edit: For the class I have
private static final long serialVersionUID = 1L;
It will break! You have to define a serialVersionUID or otherwise one will be created based on the properties of the class. So if you change the class the serialVersionUID would change aswell. If the UID don't match InvalidClassException is thrown.
Saving objects instead of the state of the object comes with alot of problems through and I recommend to save them in a more appropriate format. JSON, XML or even CSVs... Seriously that bit of overhead will save you from alot of trouble later on.
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
I am developing an app using J2ME.
The project is to store integer and string data into RMS.
The error shows that out.writeUTF(i.getNumberPlate()); is not working.
So any solution to solve this problem? The compiler cant even compile the project.
public class CarparkReservationParser {
public static byte[] parseObj(CarparkReservation i) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out;
try {
out = new DataOutputStream(baos);
// write into string
out.writeInt(i.getCarpark());
out.writeUTF(i.getNumberPlate());
out.writeUTF(i.getStudentID());
out.writeUTF(i.getName());
out.writeInt(i.getYear());
out.writeInt(i.getMonth());
// baos.toByteArray();
} catch (IOException e) {
}
// convert to byte array
return baos.toByteArray();
}
}
#jack's comment is right. writeUTF receives a String as parameter. If getNumberPlate() does not return a String you must change your code. For example:
out.writeUTF(i.getNumberPlate().toString());
I have an object that has a List each ObjectA has a few data members (strings and a byte[]). Along with a FileInputStream used to read a file into the byte array. I have initialized all data members in the global scope of the object so that I can recycle them to reduce the amount of object creation per function call. When I serialize the object my FileInputStream is null, as I store the file in the byte array. so I expect that the FileInputStream being null would be skipped by the serialization process. both the main object and the objects that are put in the list of the main object are implementing serializable
I'm able to serialize the object that holds the list of objects and deserialize it as long as the list is empty. When the list has at least one object in it. It's still able to serialize but when i attempt to deserialize it I get the following error.
IOException: writing aborted; java.io.NotSerializableException: java.io.FileInputStream
The variables in my object's is as followed:
public class MainObject implements Serializable{
private String name;
private List<ObjectA> obj;
}
public class ObjectA implements Serializable{
private String id;
private String name;
private File fileStream;
byte []data;
}
To trouble shoot this I saved the serialized object to a file and looked at it and I can see the MainObject is being saved. If I include objects into the list the objects are also being saved.
Here is the code I made for reading the files and adding them to the object.
File[] files = new File(filePath).listFiles();
for (File file : files) {
if (file.isFile()) {
System.out.println(file.getName());
try {
fin = new FileInputStream(filePath+file.getName());
ois = new ObjectInputStream(fin);
mainObjectList.add((MainObject) ois.readObject());
ois.close();
fin.close();
} catch (FileNotFoundException e) {
System.err.println("FileNotFoundException: " + e.getMessage());
} catch (ClassNotFoundException e) {
System.err.println("ClassNotFoundException: " + e.getMessage());
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
}
}
}
here is the setFile() that's in MainObject
public void setFile(String filePath) {
try {
File file=null;
fileStream = new FileInputStream(file=new File(filePath));
data = new byte[(int)file.length()];
fileStream.read(data,0,data.length);
for (int X : data){
System.out.print((char)X);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
}
Read the exception message. Contrary to the code you posted, you have a FileInputStream member, which is not serializable. Make it transient, or remove it, and construct it when you need it from the File.
And contrary to your claim that 'When i serialize mainObject and save it to a file i can see its saving all the data including the list of audioClip objects', when you serialized this data you got an exception, which you have ignored.
NB Your newly posted code:
try {
File file=null;
fileStream = new FileInputStream(file=new File(filePath));
data = new byte[(int)file.length()];
fileStream.read(data,0,data.length);
for (int X : data){
System.out.print((char)X);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
is very poor quality. It should have been written like this:
try (FileInputStream fileStream = new FileInputStream(new File(filePath))) {
data = new byte[(int)file.length()];
int total = 0;
int count;
while ((count = fileStream.read(data, total, data.length-total)) > 0) {
total += count;
}
for (byte X : data) {
System.out.print((char)X);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}
Note that you cannot assume either that a file fits into memory, that the size fits into an int, or that read() fills the buffer. You have to:
store the result of read() into a variable
test it for -1, indicating end of stream
otherwise use it as the read count, instead of the buffer size
repeat until end of stream.
Note also that you don't need the File file variable at all; that the FileInputStream should always have been a local variable; and that you weren't closing it: this code does, via the try-with-resources syntax.
I have this serializable class, wich i use to store on a binary file an ArrayList of Strings.
public class SaveState implements Serializable{
public static ArrayList <String> favoriteBusStopNumbers = new ArrayList<String>();
public static SaveState instance=new SaveState();
}
I'm using this method to store the instance with the arrayList of strings once this array is full with the data i must store:
public static void saveData(){
ObjectOutput out;
try {
//primero comprobamos si existe el directorio, y si no, lo creamos.
File folder = new File(Environment.getExternalStorageDirectory() + DIRECTORY_NAME);
if(!folder.exists())
folder.mkdirs();
File outFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
out = new ObjectOutputStream(new FileOutputStream(outFile));
out.writeObject(SaveState.instance);
out.close();
} catch (Exception e) {e.printStackTrace();}
}
And finally, i use this method on the init of my app to load the file and fill my SaveState.instance variable with the previously stored data:
public static void loadData(){
ObjectInput in;
try {
File inFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
in = new ObjectInputStream(new FileInputStream(inFile));
SaveState.instance=(SaveState) in.readObject();
in.close();
} catch (Exception e) {e.printStackTrace();}
}
When i save the data, the file is being created correctly with the object fill of data, i know it because the file has more than 0KB of disc space. But something is going wrong here because when i start my app and load the data, my SaveState.instance variable gets an empty ArrayList of strings....... then ¿what is wrong in the code?
Thanks
Your arraylist is static. Static variables are associated with the class and not with the object. So they dont get serialized.
If you do want to serialize static variables, you have to override readObject and writeObject.
Here is some additional info -- http://java.sun.com/developer/technicalArticles/Programming/serialization/
Best solution here is to reconsider your data structure and make the arraylist non-static if its saving the state of an object.
EDIT: Alternatively, you could serialize just the arraylist ( as #Ted suggests below )
The problem is that variables marked as static will not be serialized except you implement
private void readObject(ObjectInputStream ois){}
and
private void writeObject(ObjectOutputStream oos){}
ObjectOutputStream - JavaDoc:
The default serialization mechanism for an object writes the class of
the object, the class signature, and the values of all non-transient
and non-static fields.
Since the only data kept in SaveState is a static array, you'll have more success just serializing and deserializing the array:
public static void saveData(){
ObjectOutput out;
try {
//primero comprobamos si existe el directorio, y si no, lo creamos.
File folder = new File(Environment.getExternalStorageDirectory() + DIRECTORY_NAME);
if(!folder.exists())
folder.mkdirs();
File outFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
out = new ObjectOutputStream(new FileOutputStream(outFile));
out.writeObject(SaveState.favoriteBusStopNumbers);
out.close();
} catch (Exception e) {e.printStackTrace();}
}
#SuppressWarnings("unchecked")
public static void loadData(){
ObjectInput in;
try {
File inFile = new File(Environment.getExternalStorageDirectory(), DIRECTORY_NAME+"appSaveState.data");
in = new ObjectInputStream(new FileInputStream(inFile));
SaveState.favoriteBusStopNumbers=(ArrayList<String>) in.readObject();
in.close();
} catch (Exception e) {e.printStackTrace();}
}
(You need to suppress the warning about casting to a generic type.)
You don't really need the SaveState.instance field at all.