JDK-8027066 : XMLDecoder in java 7 cannot properly deserialize object arrays
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 8
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2013-10-22
  • Updated: 2014-04-16
  • Resolved: 2013-10-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8
7u60Fixed 8 b115Fixed
Related Reports
Duplicate :  
Description
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}

Comments
The same situation might be reproduced with Objects instead of arrays, when i.e. the same object is referenced from different fields. The serialized form looks pretty the same (below), but is deserialized correctly. <java version="1.8.0-ea" class="java.beans.XMLDecoder"> <object class="XMLEncoderTests$ObjectWrapper"> <void property="o1"> <object class="XMLEncoderTests$SomeClass" id="XMLEncoderTests$SomeClass0"> <void property="i"> <int>123</int> </void> </object> </void> <void property="o2"> <object idref="XMLEncoderTests$SomeClass0"/> </void> </object> </java> The reason is that the ObjectElementHandler overrides the isArgument() method: protected boolean isArgument() { return true; } while the ArrayElementHandler doesn't. The solution is to override isArgument() in the ArrayElementHandler analogously to ObjectElementHandler.
23-10-2013