XMLDecoder in Java 7 cannot properly deserialize object arrays.
In the following test case, the faulty object has an object array referenced
by two of its fields (in the example: messages and detailedMessages). As soon
as a reference is
declared over an array, the problem arises.
Note also that the serialized XML seems valid, and only the deserialization
behaves incorrectly.
Test case:
import java.beans.XMLDecoder;
import java.beans.XMLEncoder;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
public class XMLEncoderTests {
public static class Wrapper {
private String[] messages;
private String[] detailedMessages;
public String[] getDetailedMessages() {
return detailedMessages;
}
public void setDetailedMessages(String[] messages) {
this.detailedMessages = messages;
}
public String[] getMessages() {
return messages;
}
public void setMessages(String[] messages) {
this.messages = messages;
}
@Override
public String toString() {
return "Wrapper - Messages {" + Arrays.deepToString(getMessages())
+ "}, Detailed Messages {"
+ Arrays.deepToString(getDetailedMessages()) + "}";
}
@Override
public boolean equals(Object other) {
if (other instanceof Wrapper) {
Wrapper otherWrapper = (Wrapper) other;
// Compare messages and otherWrapper messages.
String[] otherMessages = otherWrapper.getMessages();
String[] messages = this.getMessages();
if (!Arrays.equals(messages, otherMessages)) {
return false;
}
// And compare detailedMessage and otherWrapperdetailedMessages.
otherMessages = otherWrapper.getDetailedMessages();
messages = this.getDetailedMessages();
if (!Arrays.equals(messages, otherMessages)) {
return false;
}
return true;
} else {
return false;
}
}
}
public static void main(String[] args) {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
XMLEncoder encoder = new XMLEncoder(stream);
Wrapper wrapper = new Wrapper();
String[] messages = new String[] { "First message", "Second message"
};
wrapper.setMessages(messages);
wrapper.setDetailedMessages(messages);
encoder.writeObject(wrapper);
encoder.close();
String output = stream.toString();
System.out.println(output);
// now decode it.
XMLDecoder decoder = new XMLDecoder(new ByteArrayInputStream
(output.getBytes()));
Object obj = decoder.readObject();
decoder.close();
// Validate object are equals.
if (!wrapper.equals(obj)) {
System.out.println("FAILURE: Objects are not equals!");
} else {
System.out.println("Objects are equals.");
}
System.out.println("Original wrapper: " + wrapper);
System.out.println("Deserialized wrapper: " + obj);
}
}
When running the above code with JDK 6, the objects are equals (i.e.
The original object and the serialized/deserialized version are the same)
and there is no bug. When running the above code with JDK 7, the objects are
not equals since the deserialized detailedMessages field is null.
Output running the above test case with JDK 7:
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.7.0_45" class="java.beans.XMLDecoder">
<object class="XMLEncoderTests$Wrapper">
<void property="detailedMessages">
<array class="java.lang.String" length="2" id="StringArray0">
<void index="0">
<string>First message</string>
</void>
<void index="1">
<string>Second message</string>
</void>
</array>
</void>
<void property="messages">
<object idref="StringArray0"/>
</void>
</object>
</java>
FAILURE: Objects are not equals!
Original wrapper: Wrapper - Messages {[First message, Second message]},
Detailed Messages {[First message, Second message]}
Deserialized wrapper: Wrapper - Messages {[First message, Second message]},
Detailed Messages {null}