XStream
  1. XStream
  2. XSTR-543

Runtime error when deserializing a field with "defined-in" attribute and alias

    Details

    • Type: Improvement Improvement
    • Status: Closed Closed
    • Priority: Minor Minor
    • Resolution: Fixed
    • Affects Version/s: 1.3.1
    • Fix Version/s: 1.4
    • Component/s: Converters
    • Labels:
      None
    • JDK version and platform:
      Sun 1.5.0_17 for Windows

      Description

      Hi

      Using XStream 1.3.1, I run into a runtime error when I deserialize a field with an alias and the attribute "defined-in". In the following, I present a simple demonstration example explaining my motivation and present a source code change that solves it.

      Here is the example:

      import java.io.Serializable;

      public class C1 implements Serializable {
      static private final long serialVersionUID = 1L;
      public String f1;

      C1(String f1)

      { this.f1 = f1; }

      }

      // ------------------------------------------------------------------

      public class C2 extends C1 {
      static private final long serialVersionUID = 1L;
      public String f2;

      C2 (String f1, String f2)

      { super(f1); this.f2 = f2; }

      }

      // ------------------------------------------------------------------

      import java.io.File;
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.InputStream;
      import java.io.OutputStream;

      import com.thoughtworks.xstream.XStream;
      import com.thoughtworks.xstream.io.xml.DomDriver;

      public class Test {
      static public void main(String[] args) throws IOException

      { File file = new File("test.xml"); serialize(file); deserialize(file); }

      static public void serialize(File file) throws IOException {
      XStream xstream = new XStream(new DomDriver());
      C2 c2 = new C2("s1", "s2");
      OutputStream os = new FileOutputStream(file);

      try

      { xstream.toXML(c2, os); }

      finally

      { os.close(); }

      }

      static public void deserialize(File file) throws IOException {
      XStream xstream = new XStream(new DomDriver());
      InputStream is = new FileInputStream(file);

      try

      { xstream.fromXML(is); }

      finally

      { is.close(); }

      }
      }

      In a normal Java Runtime Environment this produces the file "test.xml" with following contents:

      <C2>
      <f1>s1</f1>
      <f2>s2</f2>
      </C2>

      I have to use obfuscation. When I run it in an obfuscated environment, it still works, but the file contents is obfuscated:

      <pck.aab>
      <a defined-in="pck.aaa">s1</a>
      <a>s2</a>
      </pck.aab>

      Now, I want to deserialize an obfuscated xml output in a non-obfuscated environment. Thus, I use the following aliases:

      xstream.alias("pck.aaa", C1.class);
      xstream.aliasField("a", C1.class, "f1");
      xstream.alias("pck.aab", C2.class);
      xstream.aliasField("a", C2.class, "f2");

      When I run it, the following log of a runtime exception is printed:

      Exception in thread "main" com.thoughtworks.xstream.converters.ConversionException: No such field C2.f2 : No such field C2.f2
      ---- Debugging information ----
      message : No such field C2.f2
      cause-exception : com.thoughtworks.xstream.converters.reflection.ObjectAccessException
      cause-message : No such field C2.f2
      class : C2
      required-type : C2
      path : /pck.aab/a
      -------------------------------
      at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:89)
      at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:63)
      at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:76)
      at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:60)
      at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:137)
      at com.thoughtworks.xstream.core.AbstractTreeMarshallingStrategy.unmarshal(AbstractTreeMarshallingStrategy.java:33)
      at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:923)
      at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:909)
      at com.thoughtworks.xstream.XStream.fromXML(XStream.java:861)
      at TestDeserializeObfuscated.deserialize(TestDeserializeObfuscated.java:25)
      at TestDeserializeObfuscated.main(TestDeserializeObfuscated.java:12)
      Caused by: com.thoughtworks.xstream.converters.reflection.ObjectAccessException: No such field C2.f2
      at com.thoughtworks.xstream.converters.reflection.FieldDictionary.field(FieldDictionary.java:94)
      at com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider.getFieldType(PureJavaReflectionProvider.java:151)
      at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.determineType(AbstractReflectionConverter.java:350)
      at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:208)
      at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:162)
      at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:82)
      ... 10 more

      I had a look into the source code and got the impression, that the "defined-in" attribute is not considered in antialiasing. The deserialization works if I consider the class defining field in field name calculation according to the following code change in com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter#doUnmarshal(Object, HierarchicalStreamReader reader, UnmarshallingContext), line 202f:

      Class classDefiningField = determineWhichClassDefinesField(reader);
      Class fieldClass = classDefiningField == null ? result.getClass() : classDefiningField;
      String fieldName = mapper.realMember(fieldClass, originalNodeName);
      Mapper.ImplicitCollectionMapping implicitCollectionMapping = mapper.getImplicitCollectionDefForFieldName(result.getClass(), fieldName);
      boolean fieldExistsInClass = implicitCollectionMapping == null && reflectionProvider.fieldDefinedInClass(fieldName, result.getClass());

      instead of

      String fieldName = mapper.realMember(result.getClass(), originalNodeName);
      Mapper.ImplicitCollectionMapping implicitCollectionMapping = mapper.getImplicitCollectionDefForFieldName(result.getClass(), fieldName);
      Class classDefiningField = determineWhichClassDefinesField(reader);
      boolean fieldExistsInClass = implicitCollectionMapping == null && reflectionProvider.fieldDefinedInClass(fieldName, result.getClass());

      Consistently, the "defined-in" attribute would have to be considered both in aliasing and in antialiasing - if I try my example without obfuscation and with aliasing for both side, I get a file without "defined-in" and a DuplicateFieldException on deserialization. Do you see a possibility to realize such a extension it in the next XStream version?

      Thanks,
      Till

        People

        • Assignee:
          Jörg Schaible
          Reporter:
          Till Wenzinger
        • Votes:
          0 Vote for this issue
          Watchers:
          1 Start watching this issue

          Dates

          • Created:
            Updated:
            Resolved: