History | Log In     View a printable version of the current page.  
Issue Details (XML | Word | Printable)

Key: XSTR-295
Type: Bug Bug
Status: Closed Closed
Resolution: Duplicate
Assignee: Unassigned
Reporter: David Blevins
Votes: 0
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
XStream

Inheritance of inner classes do not seem to marshal both ways

Created: 12/Apr/06 04:41 AM   Updated: 07/Aug/06 09:11 PM
Component/s: None
Affects Version/s: None
Fix Version/s: 1.2

File Attachments: 1. Text File InheritanceTest.java.patch (4 kb)
2. Text File InheritedFieldAliasingMapper.java.patch (4 kb)

Issue Links:
Supercedes
 

JDK version and platform: Sun 1.5 OSX


 Description  « Hide
Running against trunk.

The added tests to InheritanceTest.java fail and to my understanding should definitely pass. These are:

public static class Person {
String firstName;
String lastName;
boolean isOld;

public Person(String firstName, String lastName, boolean old) { this.firstName = firstName; this.lastName = lastName; isOld = old; }
}

public static class House { List people = new ArrayList(); }

public static class Woman extends Person {
public Woman(String firstName, String lastName, boolean old) { super(firstName, lastName, old); }
}

public static class Man extends Person {
public Man(String firstName, String lastName, boolean old) { super(firstName, lastName, old); } }
}

public void testBasicInheritance() { xstream.alias("house", House.class); xstream.aliasField("first-name", Person.class, "firstName"); xstream.aliasField("last-name", Person.class, "lastName"); xstream.aliasField("is-old", Person.class, "isOld"); xstream.alias("woman", Woman.class); xstream.alias("man", Man.class); Man man = new Man("John", "Doe", true); Woman woman = new Woman("Jane", "Doe", true); House house = new House(); house.people.add(man); house.people.add(woman); String expected = "<house>\n" + " <people>\n" + " <man>\n" + " <first-name>John</first-name>\n" + " <last-name>Doe</last-name>\n" + " <is-old>true</is-old>\n" + " </man>\n" + " <woman>\n" + " <first-name>Jane</first-name>\n" + " <last-name>Doe</last-name>\n" + " <is-old>true</is-old>\n" + " </woman>\n" + " </people>\n" + "</house>"; assertBothWays(house, expected); }

public void testInheritanceOverriding() { xstream.alias("house", House.class); xstream.aliasField("first-name", Person.class, "firstName"); xstream.aliasField("last-name", Person.class, "lastName"); xstream.aliasField("is-old", Person.class, "isOld"); xstream.alias("woman", Woman.class); xstream.alias("man", Man.class); xstream.aliasField("is-distinguished", Man.class, "isOld"); Man man = new Man("John", "Doe", true); Woman woman = new Woman("Jane", "Doe", true); House house = new House(); house.people.add(man); house.people.add(woman); String expected = "<house>\n" + " <people>\n" + " <man>\n" + " <first-name>John</first-name>\n" + " <last-name>Doe</last-name>\n" + " <is-distinguished>true</is-distinguished>\n" + " </man>\n" + " <woman>\n" + " <first-name>Jane</first-name>\n" + " <last-name>Doe</last-name>\n" + " <is-old>true</is-old>\n" + " </woman>\n" + " </people>\n" + "</house>"; assertBothWays(house, expected); }

The result of the first test is this:

com.thoughtworks.xstream.converters.ConversionException: first$name : first$name
---- Debugging information ----
required-type : com.thoughtworks.acceptance.InheritanceTest$Man
cause-message : first$name : first$name
class : com.thoughtworks.acceptance.InheritanceTest$House
message : first$name : first$name
line number : 4
path : /house/people/man/first-name
cause-exception : com.thoughtworks.xstream.mapper.CannotResolveClassException
-------------------------------
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:63)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:45)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:46)
at com.thoughtworks.xstream.converters.collections.AbstractCollectionConverter.readItem(AbstractCollectionConverter.java:61)
at com.thoughtworks.xstream.converters.collections.CollectionConverter.populateCollection(CollectionConverter.java:57)
at com.thoughtworks.xstream.converters.collections.CollectionConverter.unmarshal(CollectionConverter.java:50)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:56)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:45)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:46)
at com.thoughtworks.xstream.annotations.AnnotationReflectionConverter.unmarshallField(AnnotationReflectionConverter.java:49)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:156)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:117)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:56)
at com.thoughtworks.xstream.core.AbstractReferenceUnmarshaller.convert(AbstractReferenceUnmarshaller.java:45)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convertAnother(TreeUnmarshaller.java:46)
at com.thoughtworks.xstream.core.TreeUnmarshaller.start(TreeUnmarshaller.java:117)
at com.thoughtworks.xstream.core.ReferenceByXPathMarshallingStrategy.unmarshal(ReferenceByXPathMarshallingStrategy.java:29)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:907)
at com.thoughtworks.xstream.XStream.unmarshal(XStream.java:895)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:772)
at com.thoughtworks.xstream.XStream.fromXML(XStream.java:765)
at com.thoughtworks.acceptance.AbstractAcceptanceTest.assertBothWays(AbstractAcceptanceTest.java:35)
at com.thoughtworks.acceptance.InheritanceTest.testThing(InheritanceTest.java:188)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at com.intellij.rt.execution.junit2.JUnitStarter.main(JUnitStarter.java:32)
Caused by: com.thoughtworks.xstream.mapper.CannotResolveClassException: first$name : first$name
at com.thoughtworks.xstream.mapper.DefaultMapper.realClass(DefaultMapper.java:49)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.XmlFriendlyMapper.realClass(XmlFriendlyMapper.java:51)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.ClassAliasingMapper.realClass(ClassAliasingMapper.java:72)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.CGLIBMapper.realClass(CGLIBMapper.java:40)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.DynamicProxyMapper.realClass(DynamicProxyMapper.java:60)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.ArrayMapper.realClass(ArrayMapper.java:76)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.MapperWrapper.realClass(MapperWrapper.java:26)
at com.thoughtworks.xstream.mapper.CachingMapper.realClass(CachingMapper.java:34)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.determineType(AbstractReflectionConverter.java:251)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.doUnmarshal(AbstractReflectionConverter.java:152)
at com.thoughtworks.xstream.converters.reflection.AbstractReflectionConverter.unmarshal(AbstractReflectionConverter.java:117)
at com.thoughtworks.xstream.core.TreeUnmarshaller.convert(TreeUnmarshaller.java:56)
... 40 more



 All   Comments   Change History      Sort Order: Ascending order - Click to sort in descending order
Joerg Schaible - 12/Apr/06 05:26 AM
Hello David,

first I want to state, that it is difficult to address two separate topics with one JIRA issue. The state of this issue now reflects the anser to your fist case.

1) You've been hit by an internal mechanism to ensure valid XML tags. Inner classes are named by Java with a dollar sign in their class names. This character is not legal for XML tags. Therefore a dollar is mapped to a minus while serializing the objects and in return mapped back to dollar unserializing the XML. Unfortunately your aliases infer with this mechanism, since they contain also a minus. We admit, that this is not at all obvious for a user, but the culprit is the name mapping mechanism, which is addressed by XSTR-252. Therefore this issue is classified as "duplicate".

2) The second test is not valid and I would have closed it as "won't fix". We cannot support such an inherited alias mechanism for two main reasons:

  • the possibility of Java to overload a member with the same name. In this case we can no longer the alias definition would be ambiguous.
  • depending on the converter base classes can be handled completly differently. E.g. the SerializationConverter will un der some circumstances write the members of the base class into an individual section. Again we will be then in a situation, where the definition of an overloaded alias is no longer clear.

Nevertheless thank you for your test cases, they allowed an immediate analysis of the situation.

  • Jörg

David Blevins - 13/Apr/06 04:58 AM
I know you don't want, but I figured I'd offer anyway. At the very least it will sit here in this Jira and if someday minds change, it will be ready and waiting.

My perspective: Plain fields represent 80% (rough estimate) of the xml elements I output/input. Even if the other 20% can't easily support even simple inheritance, I don't mind. This at least saves me a couple hundred lines of redundant 'aliasField' calls.