XStream

deserializing Enums incorrectly

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: None
  • Fix Version/s: 1.1.3
  • Component/s: None
  • Labels:
    None
  • JDK version and platform:
    1.5.1-01 and fedora core 3

Description

It appears that XStream is not deserializing Enums correctly.

Here is a test for proof:

public enum MyEnum {
Test1(), Test2();

MyEnum() {

}

// public Object readResolve() { // return MyEnum.valueOf(this.name()); // }
}

MyEnum test1 = MyEnum.Test1;
System.out.println(test1.equals(xStream.fromXML(xStream.toXML(test1))));

If you run the previous two lines, it will print out false signifying that test1 is not equal to itself when ran through XStream. Now if you uncomment the readResolve method it works just fine, however, XStream should return the correct Enum. The problem exists in the ObjectInputStream. If you read the java docs on Enums in the ObjectInputStream, the ois should call the static method Enum.valueOf(Class, String).

Activity

Hide
Bryan Coleman added a comment -

Here is a solution:

You could add an EnumConverter.

public class EnumConverter extends Converter {
public boolean canConvert(Class type) { return type.isEnum(); }

public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { writer.setValue(((Enum)source).name()); }

public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { return Enum.valueOf(context.getRequiredType(), reader.getValue()); }
}

Show
Bryan Coleman added a comment - Here is a solution: You could add an EnumConverter. public class EnumConverter extends Converter { public boolean canConvert(Class type) { return type.isEnum(); } public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { writer.setValue(((Enum)source).name()); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { return Enum.valueOf(context.getRequiredType(), reader.getValue()); } }
Hide
Bryan Coleman added a comment -

Here is a version with support for enums with methods.

public class EnumConverter extends Converter {
public boolean canConvert(Class type) { return type.isEnum() || Enum.class.isAssignableFrom(type); }

public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { writer.setValue(((Enum)source).name()); }

public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { return Enum.valueOf(context.getRequiredType().getSuperclass(), reader.getValue()); }
}

Show
Bryan Coleman added a comment - Here is a version with support for enums with methods. public class EnumConverter extends Converter { public boolean canConvert(Class type) { return type.isEnum() || Enum.class.isAssignableFrom(type); } public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { writer.setValue(((Enum)source).name()); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { return Enum.valueOf(context.getRequiredType().getSuperclass(), reader.getValue()); } }
Hide
Bryan Coleman added a comment -

Yet another fix:

public class EnumConverter extends Converter {
public boolean canConvert(Class type) { return type.isEnum() || Enum.class.isAssignableFrom(type); }

public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { writer.setValue(((Enum)source).name()); }

public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Class c = context.getRequiredType().getSuperclass() == Enum.class ? context.getRequiredType() : context.getRequiredType().getSuperclass(); return Enum.valueOf(c, reader.getValue()); }
}

Show
Bryan Coleman added a comment - Yet another fix: public class EnumConverter extends Converter { public boolean canConvert(Class type) { return type.isEnum() || Enum.class.isAssignableFrom(type); } public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) { writer.setValue(((Enum)source).name()); } public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) { Class c = context.getRequiredType().getSuperclass() == Enum.class ? context.getRequiredType() : context.getRequiredType().getSuperclass(); return Enum.valueOf(c, reader.getValue()); } }
Hide
Bryan Coleman added a comment -

I am using version 1.1.1 with xpp3_min-1.1.3.4.I.jar

Show
Bryan Coleman added a comment - I am using version 1.1.1 with xpp3_min-1.1.3.4.I.jar
Hide
Bryan Coleman added a comment -

Here is a UnitTest:

public class XStreamTest extends TestCase {
private XStream xStream;

protected void setUp() throws Exception { super.setUp(); xStream = new XStream(); xStream.registerConverter(new EnumConverter()); }

public void test() throws Exception { User user = PicapsResourceFactory.getInstance().getUserRegistry().getUser("plantfloor"); assertEquals(user, xStream.fromXML(xStream.toXML(user))); }

public void testEnum() throws Exception { assertEquals(SimpleEnum.Test1, xStream.fromXML(xStream.toXML(SimpleEnum.Test1))); assertSame(SimpleEnum.Test1, xStream.fromXML(xStream.toXML(SimpleEnum.Test1))); assertEquals(AnotherEnum.Test1, xStream.fromXML(xStream.toXML(AnotherEnum.Test1))); assertSame(AnotherEnum.Test1, xStream.fromXML(xStream.toXML(AnotherEnum.Test1))); }

private enum SimpleEnum { Test1, Test2; }

private enum AnotherEnum {
Test1() {
Object getObj() { return new Object(); }
}, Test2() {
Object getObj() { return new Object(); } }
};

abstract Object getObj();
}
}

Show
Bryan Coleman added a comment - Here is a UnitTest: public class XStreamTest extends TestCase { private XStream xStream; protected void setUp() throws Exception { super.setUp(); xStream = new XStream(); xStream.registerConverter(new EnumConverter()); } public void test() throws Exception { User user = PicapsResourceFactory.getInstance().getUserRegistry().getUser("plantfloor"); assertEquals(user, xStream.fromXML(xStream.toXML(user))); } public void testEnum() throws Exception { assertEquals(SimpleEnum.Test1, xStream.fromXML(xStream.toXML(SimpleEnum.Test1))); assertSame(SimpleEnum.Test1, xStream.fromXML(xStream.toXML(SimpleEnum.Test1))); assertEquals(AnotherEnum.Test1, xStream.fromXML(xStream.toXML(AnotherEnum.Test1))); assertSame(AnotherEnum.Test1, xStream.fromXML(xStream.toXML(AnotherEnum.Test1))); } private enum SimpleEnum { Test1, Test2; } private enum AnotherEnum { Test1() { Object getObj() { return new Object(); } }, Test2() { Object getObj() { return new Object(); } } }; abstract Object getObj(); } }
Hide
Joe Walnes added a comment -

Thanks Bryan. Enum support had already been added for XSTR-70, however this did not address the polymorphic enums. Integrating your changes finished this.

I have also added EnumMapper that ensures that the polymorphic enums don't get converted into the $1 classes, meaning the XML is cleaner and aliases can be used for enums.

I've credited your work in the code and added you to the contributors page.

Show
Joe Walnes added a comment - Thanks Bryan. Enum support had already been added for XSTR-70, however this did not address the polymorphic enums. Integrating your changes finished this. I have also added EnumMapper that ensures that the polymorphic enums don't get converted into the $1 classes, meaning the XML is cleaner and aliases can be used for enums. I've credited your work in the code and added you to the contributors page.
Hide
Joerg Schaible added a comment -

No further comment from reporter

Show
Joerg Schaible added a comment - No further comment from reporter

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: