我正在使用XStream。目前用其他东西替换XStream并不容易。
我有一个接口(MyInterface)和几个实现该接口的子类(在下面的示例代码中,有一个名为MyImplementation)。
我想序列化和反序列化子类的实例。我发现如果将class属性放入XML中,我可以反序列化:
<myInterfaceElement class="myPackage.MyImplementation">
<field1>value1</field1>
<field2>value2</field2>
</myInterfaceElement>
但是,我不知道如何让XStream编写class属性。如何在序列化时让XStream包含class属性?或者是否有另一种方法来序列化/反序列化类层次结构,以便所有实现的元素名称相同,并且每个子类可以定义自己的字段?
这是一个MyInterface的示例,MyImplementation,一个试图让它工作的JUnit测试用例。当classAttributeSetInResult失败时,deserializeWithClassAttribute测试通过。
package myPackage;
public interface MyInterface {
}
package myPackage;
public class MyImplementation implements MyInterface {
public String field1;
public String field2;
public MyImplementation(String field1, String field2) {
this.field1 = field1;
this.field2 = field2;
}
}
package myPackage;
import org.junit.Test;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;
import static org.junit.Assert.*;
public class xstreamTest {
@Test
public void classAttributeSetInResult() {
MyInterface objectToSerialize = new MyImplementation("value1", "value2");
final XStream xStream = new XStream(new DomDriver());
xStream.alias("myInterfaceElement", MyInterface.class);
String xmlResult = xStream.toXML(objectToSerialize).toString();
String expectedResult =
"<myInterfaceElement class=\"myPackage.MyImplementation\">\n" +
" <field1>value1</field1>\n" +
" <field2>value2</field2>\n" +
"</myInterfaceElement>";
assertEquals(expectedResult, xmlResult);
}
@Test
public void deserializeWithClassAttribute() {
String inputXmlString =
"<myInterfaceElement class=\"myPackage.MyImplementation\">\r\n" +
" <field1>value1</field1>\r\n" +
" <field2>value2</field2>\r\n" +
"</myInterfaceElement>";
final XStream xStream = new XStream(new DomDriver());
MyInterface result = (MyInterface)xStream.fromXML(inputXmlString);
assertTrue("Instance of MyImplementation returned", result instanceof MyImplementation);
MyImplementation resultAsMyImplementation = (MyImplementation)result;
assertEquals("Field 1 deserialized", "value1", resultAsMyImplementation.field1);
assertEquals("Field 2 deserialized", "value2", resultAsMyImplementation.field2);
}
}
答案 0 :(得分:1)
我通过以下方式解决了这个问题(感谢McD提示使用转换器):
添加扩展ReflectionConverter的自定义转换器:
package myPackage;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.reflection.ReflectionConverter;
import com.thoughtworks.xstream.converters.reflection.ReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.mapper.Mapper;
public class MyInterfaceConverter extends ReflectionConverter {
public MyInterfaceConverter(Mapper mapper, ReflectionProvider reflectionProvider) {
super(mapper, reflectionProvider);
}
@Override
public void marshal(Object original, final HierarchicalStreamWriter writer, final MarshallingContext context) {
writer.addAttribute("class", original.getClass().getCanonicalName());
super.marshal(original, writer, context);
}
@SuppressWarnings("rawtypes")
@Override
public boolean canConvert(Class type) {
return MyInterface.class.isAssignableFrom(type);
}
}
设置xStream时注册新的转换器:
@Test
public void classAttributeSetInResult() {
MyInterface objectToSerialize = new MyImplementation("value1", "value2");
final XStream xStream = new XStream(new DomDriver());
xStream.alias("myInterfaceElement", MyImplementation.class);
xStream.registerConverter(new MyInterfaceConverter(xStream.getMapper(), xStream.getReflectionProvider()));
String xmlResult = xStream.toXML(objectToSerialize).toString();
String expectedResult =
"<myInterfaceElement class=\"myPackage.MyImplementation\">\n" +
" <field1>value1</field1>\n" +
" <field2>value2</field2>\n" +
"</myInterfaceElement>";
assertEquals(expectedResult, xmlResult);
}
希望这会帮助其他人。如果有人有更好的想法,请告诉我!
答案 1 :(得分:0)
我会使用自定义转换器来解决此问题:
您的课程/接口:
public static interface MyInterface {
public String getField1();
public String getField2();
}
public static class MyImplementation implements MyInterface {
public String field1;
public String field2;
public MyImplementation(String field1, String field2) {
this.field1 = field1;
this.field2 = field2;
}
public String getField1() { return field1; }
public String getField2() { return field2; }
}
相当快速的&amp;脏转换器:
public static class MyInterfaceConverter implements Converter {
private static final String ATTR_NAME_CLASS = "concrete-class";
private static final String NODE_NAME_FIELD1 = "field1";
private static final String NODE_NAME_FIELD2 = "field2";
public boolean canConvert(Class type)
{
return type.equals(MyImplementation.class);
}
public void marshal(Object obj, HierarchicalStreamWriter writer, MarshallingContext context)
{
if (obj == null)
// no need to save null-objects
return;
final String type = obj.getClass().getSimpleName();
final MyInterface myInterface;
if (obj instanceof MyImplementation)
myInterface = (MyInterface) obj;
// else if (...)
// ...
else
throw new IllegalArgumentException("Cannot convert objects of type " + obj.getClass());
writer.addAttribute(ATTR_NAME_CLASS, type);
marshalAttribute(writer, context, NODE_NAME_FIELD1, myInterface.getField1());
marshalAttribute(writer, context, NODE_NAME_FIELD2, myInterface.getField2());
}
private static void marshalAttribute(HierarchicalStreamWriter writer, MarshallingContext context, String attrName, Object val)
{
if (val != null) {
writer.startNode(attrName);
context.convertAnother(val);
writer.endNode();
}
}
public Object unmarshal(HierarchicalStreamReader reader,
UnmarshallingContext context)
{
final String type = reader.getAttribute(ATTR_NAME_CLASS);
String field1Value = null, field2Value = null;
while (reader.hasMoreChildren()) {
reader.moveDown();
if (NODE_NAME_FIELD1.equals(reader.getNodeName()))
field1Value = (String)context.convertAnother(null, String.class);
else if (NODE_NAME_FIELD2.equals(reader.getNodeName()))
field2Value = (String)context.convertAnother(null, String.class);
reader.moveUp();
}
if (MyImplementation.class.getSimpleName().equals(type)) {
return new MyImplementation(field1Value, field2Value);
}
throw new IllegalArgumentException("Cannot unmarshal objects of type " + type);
}
}
test / usage / XStream-Initialization:
@Test
public void classAttributeSetInResult() {
MyInterface objectToSerialize = new MyImplementation("value1", "value2");
final XStream xStream = new XStream(new DomDriver());
xStream.alias("myInterfaceElement", MyImplementation.class);
// xStream.alias("myInterfaceElement", OtherImplementation.class);
xStream.registerConverter(new MyInterfaceConverter());
String xmlResult = xStream.toXML(objectToSerialize).toString();
String expectedResult =
"<myInterfaceElement concrete-class=\"MyImplementation\">\r\n" +
" <field1>value1</field1>\r\n" +
" <field2>value2</field2>\r\n" +
"</myInterfaceElement>";
assertEquals(expectedResult, xmlResult);
}
@Test
public void deserializeWithClassAttribute() {
String inputXmlString =
"<myInterfaceElement concrete-class=\"MyImplementation\">\r\n" +
" <field1>value1</field1>\r\n" +
" <field2>value2</field2>\r\n" +
"</myInterfaceElement>";
final XStream xStream = new XStream(new DomDriver());
xStream.alias("myInterfaceElement", MyImplementation.class);
// xStream.alias("myInterfaceElement", OtherImplementation.class);
xStream.registerConverter(new MyInterfaceConverter());
MyInterface result = (MyInterface)xStream.fromXML(inputXmlString);
assertTrue("Instance of MyImplementation returned", result instanceof MyImplementation);
MyImplementation resultAsMyImplementation = (MyImplementation)result;
assertEquals("Field 1 deserialized", "value1", resultAsMyImplementation.field1);
assertEquals("Field 2 deserialized", "value2", resultAsMyImplementation.field2);
}