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
Related
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.
I have a method that writes data from a list to a file, a method that reads data from a file into a list and a method that writes data from a list in a file to the specified number of times. I'm trying to extract data from a file after I use the first method writeFile () everything works fine. I read the data from the file into the list by readFile () method. After that I use my method which writes to the file the number of times I need, everything is fine, it writes multyWrite (). But after that I can not read the data from the file in the readFile () method since I get `
Exception stack trace:
Exception in thread "main" java.io.StreamCorruptedException: invalid type code: AC
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1599)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)
at ProductService.readFile(ProductService.java:47)
at Main.main(Main.java:21)
I know that I should use objectOutputStream.reset (), but where would it be better to use it?
private String fileName;
private ProductInterface<FlyingMachine> productService = new ProductInterfaceImpl();
private ObjectOutputStream objectOutputStream;
private FileOutputStream fileOutputStream;
public ProductService(String fileName) throws IOException {
this.fileName = fileName;
fileOutputStream = new FileOutputStream(fileName);
this.objectOutputStream = new ObjectOutputStream(fileOutputStream);
}
public void writeFile() throws IOException {
try {
for (FlyingMachine f : productService.getProductContainer()) {
objectOutputStream.writeObject(f);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (objectOutputStream != null) {
objectOutputStream.flush();
objectOutputStream.close();
fileOutputStream.close();
}
}
}`
public void readFile() throws IOException {
ObjectInputStream objectInputStream = null;
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(fileName);
objectInputStream = new ObjectInputStream(fileInputStream);
while (fileInputStream.available() > 0) {
FlyingMachine flyingMachine = (FlyingMachine) objectInputStream.readObject();
productService.getProductContainer().add(flyingMachine);
}
} catch (ClassNotFoundException | EOFException e) {
e.printStackTrace();
} finally {
if (objectInputStream != null) {
objectInputStream.close();
fileInputStream.close();
}
}
}
public void multyWrite(int number) throws IOException {
for (int i = 0; i < number; i++) {
try {
fileOutputStream = new FileOutputStream(fileName, true);
objectOutputStream = new ObjectOutputStream(fileOutputStream);
for (FlyingMachine f : productService.getProductContainer()) {
objectOutputStream.writeObject(f);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (objectOutputStream != null) {
objectOutputStream.flush();
objectOutputStream.close();
}
}
}
}
You create a new ObjectOutputStream in the constructor. In writeFile you use that OOS instance and close it. But in multyWrite you don't use it and instead create new instances.
Now when you call multyWrite without having called writeFile first, that first OOS will still be open, but the OOS you create in multyWrite doesn't know that - thus causing your file to have two OOS headers after another.
And then when you try to read such a file, the ObjectInputStream will find the first header (all is fine) and then unexpectedly find the second header, while it expected a type code. That header starts with 0xAC, hence throwing the exception message "invalid type code: AC".
To fix this, either have multyWrite use the OOS constructed in your constructor, the same way writeFile does, or make sure that that OOS is closed before you create a new one.
It's generally not a good idea to open a stream (of any kind) in a constructor and then rely on external code calling a specific method to close it. Better create streams when you need them and close them directly.
I would like to append Serializable objects in a existing file, but apparently it deletes the previous stored objects.
For the parameter File F, I send new File(file's path). Maybe my mistake is from there ?
Thank you for helping.
public static void wrinting(File[] tab, File f) throws Exception{
ArrayList<ImageClass> obj = imagesArray(tab);
ObjectOutputStream oos;
try {
oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
for (ImageClass i : obj) {
oos.writeObject(i);
}
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Open the FileOutputStream like this instead:
new FileOutputStream(f, true)
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).
Here's my current code:
//export method
public static void exportObj (Object obj, String fname) {
try {
// Serialize data object to a file
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fname));
out.writeObject(obj);
out.close();
} catch (IOException e) {}
}
//import method
public static Object importObj (String fname) {
try {
ObjectInputStream in = new ObjectInputStream(new FileInputStream(fname));
return in.readObject();
} catch (IOException e) {}
return new Object();
}
The export function works fine, I think, it turns my User object into a file and saves it, but then when I try to import it, it gives me a ClassNotFound Exception. What is happening?
All the classes you want to deserialize must exist on the CLASSPATH of the project that contains the import code.