簡體   English   中英

如何使用XStream在類型層次結構中序列化/反序列化對象?

[英]How do I, using XStream, serialize/deserialize objects in a type hierarchy?

我正在使用XStream。 目前,用其他東西替換XStream還不容易。

我有一個接口(MyInterface)和幾個實現該接口的子類(在下面的示例代碼中,有一個稱為MyImplementation)。

我想序列化和反序列化子類的實例。 我發現如果將class屬性放入XML中,我可以反序列化就好了:

<myInterfaceElement class="myPackage.MyImplementation">
  <field1>value1</field1>
  <field2>value2</field2>
</myInterfaceElement>

但是,我不知道如何讓XStream編寫class屬性。 序列化時如何讓XStream包含class屬性? 還是有另一種方法來對類層次結構進行序列化/反序列化,以便所有實現的元素名稱都相同,並且每個子類都可以定義自己的字段?

這是MyInterface,MyImplementation的示例,這是一個試圖使其工作的JUnit測試用例。 deserializeWithClassAttribute測試通過,而classAttributeSetInResult失敗。


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);
    }
}

我通過執行以下操作解決了這個問題(感謝使用McD的提示使用Converter):

  1. 添加一個擴展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); } } 
  2. 設置xStream時注冊新的Converter:

     @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); } 

希望這會幫助其他人。 如果有人有更好的主意,請告訴我!

我將使用自定義轉換器來解決此問題:

您的類/接口:

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; }
}

相當快速和骯臟的轉換器:

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);
    }
}

測試/使用/ XStream初始化:

@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);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM