I understand the theory behind incompatible serialVersionUIDs (i.e. you can discriminate different compilation versions of the same class) but I am seeing an issue that I don't understand and doesn't fall into the obvious error causes (different compiled version of the same class).
I am testing a serialization/deserialization process. All code is running on one machine, in the same VM, and both serialization and deserialization methods are using the same version of the compiled class. Serialization works fine. The class being serialized is quite complex, contains a number of other classes (java types and UDTs), and contains reference cycles. I haven't declared my own UID in any class.
This is the code:
public class Test {
public static void main(String[] args) throws Exception {
ContextNode context = WorkflowBuilder.getSimpleSequentialContextNode();
String contextString = BinarySerialization.serializeToString(context);
ContextNode contextD = BinarySerialization.deserializeFromString(ContextNode.class, contextString);
}
}
public class BinarySerialization {
public static synchronized String serializeToString(Object obj) throws Exception {
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(byteStream);
oos.writeObject(obj);
oos.close();
return byteStream.toString();
}
public static synchronized <T> T deserializeFromString(Class<T> type, String byteString) throws Exception {
T object = null;
ByteArrayInputStream byteStream = new ByteArrayInputStream(byteString.getBytes());
ObjectInputStream in = new ObjectInputStream(byteStream);
object = (T)in.readObject();
in.close();
return object;
}
}
I am getting an InvalidClassException (local class incompatible: stream classdesc serialVersionUID = -7189235121689378989, local class serialVersionUID = -7189235121689362093) when deserializing.
What is the underlying issue? And how should I fix it?
Thanks
Edit
I should state the purpose of this. The serialized data will both need to be stored in a sqlite database and sent across the wire to other clients. If String is the wrong format for passing around the serialized data, what should I be using instead that will let me store and pass the data about? Thanks again.
First rule: never use String or char[] or Reader or Writer when handling binary data.
You're handling binary data and try to put it into a String. Don't do that, that's an inherently broken operation.
Next: the return value of byteStream.toString() doesn't in any way represent the actual content of the ByteArrayOutputStream. You'll want to use .getBytes() and pass the byte[] around (remember: treat binary data as binary data and not as a String).
Related
I have a serializable class with custom writeObject() and readObject() methods.
When an object serializes, it needs to write two byte arrays, one after another. When something deserializes it, it needs to read those two arrays.
This is my code:
private void writeObject (final ObjectOutputStream out) throws IOException {
..
out.writeByte(this.signature.getV()); //one byte
out.writeObject(this.signature.getR()); //an array of bytes
out.writeObject(this.signature.getS()); //an array of bytes
out.close();
}
private void readObject (final ObjectInputStream in) throws IOException, ClassNotFoundException {
..
v = in.readByte();
r = (byte[])in.readObject();
s = (byte[])in.readObject();
this.signature = new Sign.SignatureData(v, r, s); //creating a new object because
//sign.signaturedata
// is not serializable
in.close();
}
When the object is being deserialized (readObject method) it throws an EOFException and all three variables are null/undefined.
Relating to question title, I saw a class called ByteArrayOutputStream, but to use it, it has to be enclosed in a ObjectOutputStream, which I cannot do, ad I have an OutputStream given and must write with it.
1. How do one properly write a byte array using objectOutputStream and properly reads it using ObjectInputStream?
2. Why the code above throws an EOFException without reading even one variable?
EDIT: I need to clarify: the readObject() and writeObject() are called by jvm itself while deserializing and serializing the object.
The second thing is, the SignatureData is a subclass to Sign, that comes from a third-party library - and that's why it's not serializable.
The third thing is, the problem probably lies in the reading and writing byte arrays by ObjectInput/ObjectOutput streams, not in the Sign.SignatureData class.
I would like to know how to save an ArrayList of abstract Objects to a file.
So far I only save primitive types or ArrayLists of primitive types by converting them to a comma separated String and storing this with a buffered reader.
But now I have got an ArrayList of Game Elements, which have really different properties and Constructors, so my normal approach won't work. There has to be something nicer than storing each to a file or each type of Object to a file or add plenty of seperator levels.
How do I do this in a nice way?
Have a look at Serialization, there are plenty of tutorials out there so I am not going to post any code:
http://www.tutorialspoint.com/java/java_serialization.htm
You can not instantiate Abstract Objects so you will need a child class which extends it. Also Abstract class should implement Serialize. Then using ObjectOutputStream you can directly write ArrayList using writeObject() method.
Below is the sample application
public abstract class Parent implements Serializable {
public abstract String getValue(); //Just to show value persist
}
public class Child extends Parent {
String value = null;
Child(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
// No throws clause here
public static void main(String[] args) throws FileNotFoundException,
IOException, ClassNotFoundException {
//create Arraylist
ArrayList<Parent> parents = new ArrayList<Parent>();
parents.add(new Child("test"));
//store
ObjectOutputStream objectOutputStream = new ObjectOutputStream(
new FileOutputStream("test.txt"));
objectOutputStream.writeObject(parents);
objectOutputStream.close();
//Read back
ObjectInputStream objectInputStream = new ObjectInputStream(
new FileInputStream("test.txt"));
ArrayList<Parent> readObjects = (ArrayList<Parent>)objectInputStream.readObject();
System.out.println(readObjects.get(0).getValue());
}
The answer could be two.
Depending on what is the usage of the file later.
ANS 1: It you want the object values to be saved temporarily in the file and reload it again from the file, then serialization is the best options.
ANS 2: If the file is output of the program and then you try the below
option#1: Start each line in the file with the unique object name
OBJECT1, blue, pink, yellow....
OBJECT2, rose, dairy, sunflower, cauliflower..
option#2 instead of the flat file(txt) you can use an apache poi framework to write
the object in more organised way.
Say, I have a class X which has a field value, that is,
class X implements Serializable {
private int value;
// ...
}
Further it has getters and setters not displayed here. This class is serialized.
At the deserialzation, end same class has value field and access specifier is public. Further, this class does not have getters and setters. So, my questions are:
Does deserialization fail in case the access specifier of the field changes OR some or all of the methods go missing in the class at the deserialization end?
What is the mechanism by which fields are assigned their values during deserialization?
Some good links The Java serialization algorithm revealed
1) does deserialization fail in case the access specifier of the field
changes OR some or all of the methods go missing in the class at the
deserialization end ?
Serialization happens using Using Reflection
Java Detects the changes to a class using the
private static final long serialVersionUID
The default involves a hashcode. Serialization creates a single hashcode, of type long, from the following information:
The class name and modifiers
The names of any interfaces the class implements
Descriptions of all methods and constructors except private methods and constructors
Descriptions of all fields except private, static, and private transient
The default behavior for the serialization mechanism is a classic "better safe than sorry" strategy. The serialization mechanism uses the suid, which defaults to an extremely sensitive index, to tell when a class has changed. If so, the serialization mechanism refuses to create instances of the new class using data that was serialized with the old classes.
2) what is the mechanism by which fields are assigned their values
during deserialization ?
The real details can be read in the Java Object Serialization Specification.
To answer your questions:
Serialization has a basic sanity check to see if the serialization ends use the same version of a class: the serialVersionUID member must be equal. Read the section Stream Unique Identifiers to know more about it. Basically, it's a static value which you can either manage yourself by declaring it on your class, or let the compiler generate one for you. If the compiler generates it, ANY change to a class will result in a change of serialVersionUID and hence will make the deserialization fail if the ends do not have exactly the same classes. If you want to avoid this, declare the variable yourself and update it manually when a change to the class' member variables does make classes incompatible.
The Java Virtual Machine does a lot of the magic here, it can access all internal state directly without the need for getters (fields marked transient or static aren't serialized though). Also, while the Serializable interface doesn't specify any methods to implement, there are a number of 'magic methods' which you can declare to influence the serialization process. Read section "The writeObject Method" and onwards to know more. Be aware though that you should use these sparingly as they might confuse any maintenance developers!
You don't need to have getters/setter to serialize/deserialize using java serialization, for example, check this code:
public class Main {
public static class Q implements Serializable {
private int x;
public Q() {
x = 10;
}
public void printValue() {
System.out.println(x);
}
}
public static void main(String[] args) throws Exception {
Q q = new Q();
FileOutputStream fos = new FileOutputStream("c:\\temp.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(q);
fos.close();
FileInputStream fis = new FileInputStream("c:\\temp.out");
ObjectInputStream oin = new ObjectInputStream(fis);
Q q2 = (Q)oin.readObject();
fis.close();
q2.printValue();
}
}
I don't really know how you get this results, but what you tell is not the default behaviour of serialisation. So, I guess you are using it wrong. Here is some sample code:
public class X implements Serializable
{
private int value;
public int getValue() { return value; }
}
Here the serialisation/deserialisation process:
X x = new X();
x.setValue(4);
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputSteam(buffer);
oos.writeObject(x);
oos.flush();
oos.close();
ByteArrayInputStream in = new ByteArrayInputStream(buffer.toByteArray());
ObjectInputStream ois = new ObjectInputStream(in);
Object obj = ois.readObject();
if (obj instanceof X)
{
X readObject = (X) obj;
System.out.println(readObject.getValue());
}
You probably used Java Reflection to get your results. Make sure you use getDeclaredFields(); and getDeclaredMethods(); instead of the variants without Declared in the method name.
Does deserialization fail in case the access specifier of the field changes
No.
OR some or all of the methods go missing in the class at the deserialization end?
Yes, unless the receiving class has a serialVersionUID member whose value equals the value encoded in the stream.
what is the mechanism by which fields are assigned their values during deserialization?
Too broad, but:
Reflection, and
name matching (rather than matching by position in the class and stream).
I'm trying to use javax.crypto.Cipher.doFinal(byte[]) method to encrypt an object. But, for security reasons, the object cannot be serializable.
So, how to convert the object to byte array without serialization?
--update
is using serialization the only way to use this Cipher method? Because as I know important data should not be serializable.
I used com.fasterxml.jackson.databind.ObjectMapper.
private static byte[] serialize(Object obj) throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
mapper.writeValue(os, obj);
return os.toByteArray();
}
You just serialize each of it's components. Recurse. Eventually you end up with native objects that you can serialize.
If you implement this by implementing java's serialization methods, java will ensure that you do not serialize any object twice and will take care of references for you.
In short, make the object serializable.
Solved,
instead of use a getByteArray() to call Cipher.doFinal(), I'll use Cipher.doFinal() inside the class, with a getEncryptedByteArray() method; so I serialize the data inside the class without making the class itself serializable, and the return result will be encrypted.
Any objection to this approach will be considered.. :)
Here is a simple example of serializing a class to a byte array.
public Class Foo {
private boolean isHappy;
private short happyCount;
private Bar bar;
public byte[] serializeData () throws IOException
{
ByteArrayOutputStream stream = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream( stream );
out.writeBoolean(isHappy);
out.writeShort( slope );
// Serialize bar which will just append to this byte stream
bar.doSerializeData(out);
// Return the serialized object.
byte[] data = stream.toByteArray();
// Clean up.
stream.close();
return data;
}
}
Of course, a lot of the details in your case depend on your class structure but hopefully this gets you pointed in the right direction.
To deserialize you just need to reverse the above.
java.beans.XMLEncoder/Decoder.
I'm writing a network app, which sends and receives a lot of different kinds of binary packets, and I'm trying to make adding new kinds of packets to my app as easy as possible.
For now, I created a Packet class, and I create subclasses of it for each different kind of packet. However, it isn't as clean as it seems; I've ended up with code like this:
static class ItemDesc extends Packet {
public final int item_id;
public final int desc_type;
public final String filename;
public final String buf;
public ItemDesc(Type t, int item_id, int desc_type, String filename, String buf) {
super(t); // sets type for use in packet header
this.item_id = item_id;
this.desc_type = desc_type;
this.filename = filename;
this.buf = buf;
}
public ItemDesc(InputStream i) throws IOException {
super(i); // reads packet header and sets this.input
item_id = input.readInt();
desc_type = input.readByte();
filename = input.readStringWithLength();
buf = input.readStringWithLength();
}
public void writeTo(OutputStream o) throws IOException {
MyOutputStream dataOutput = new MyOutputStream();
dataOutput.writeInt(item_id);
dataOutput.writeByte(desc_type);
dataOutput.writeStringWithLength(filename);
dataOutput.writeStringWithLength(buf);
super.write(dataOutput.toByteArray(), o);
}
}
What bothers me about this approach is the code repetition - I'm repeating the packet structure four times. I'd be glad to avoid this, but I can't see a reasonable way to simplify it.
If I was writing in Python I would create a dictionary of all possible field types, and then define new packet types like this:
ItemDesc = [('item_id', 'int'), ('desc_type', 'byte'), ...]
I suppose that I could do something similar in any functional language. However, I can't see a way to take this approach to Java.
(Maybe I'm just too pedantic, or I got used to functional programming and writing code that writes code, so I could avoid any repetition :))
Thank you in advance for any suggestions.
I agree with #silky that your current code is a good solution. A bit of repetitious (though not duplicated) code is not a bad thing, IMO.
If you wanted a more python-like solution, you could:
Replace the member attributes of ItemDesc with some kind of order-preserving map structure, do the serialization using a common writeTo method that iterates over the map. You also need to add getters for each attribute, and replace all uses of the existing fields.
Replace the member attributes with a Properties object and use Properties serialization instead of binary writes.
Write a common writeTo method that uses Java reflection to access the member attributes and their types and serialize them.
But in all 3 cases, the code will be slower, more complicated and potentially more fragile than the current "ugly" code. I wouldn't do this.
Seem okay to me. You may just want to abstract some of the 'general' parts of the packet up the inheritance chain, so you don't need to read them, but it makes sense to be repeating the format like you are, because you've got a case for reading in raw from the constructor, reading from a stream, and writing. I see nothing wrong with it.
I am not sure you can do this in java- but maybe you could reuse one of the ctors:
public ItemDesc(InputStream i) throws IOException {
super(i); // reads packet header and sets this.input
this(input.readInt(), input.readByte(), input.readStringWithLength(), input.readStringWithLength());
}
Were 'this' means a call to this classes ctor, whtever the syntax might be.