1

假设我有一个名为的抽象类AbstractItem,用作另一个类中的字段。当我使用 XStream 生成 XML 时,我希望元素标记基于AbstractItem.

我得到什么:

<Test>
  <item class="Item1" name="name 1" description="description 1"/>
</Test>

我想要的是:

<Test>
  <Item1 name="name 1" description="description 1"/>
</Test>

XStream我尝试通过执行以下操作在实例上设置别名:

stream.alias("Item1", Item1.class);

并且还使用:

stream.aliasType("Item1", Item1.class);

以上任何一种都不起作用。


为了清楚起见,这里是上述的可运行示例:

测试.java

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("Test")
public class Test {

    public AbstractItem item;   

    public static void main(String[] args){

        Test t1 = new Test();
        Item1 item1 = new Item1();
        item1.name = "name 1";
        item1.description = "description 1";
        t1.item = item1;

        XStream stream = new XStream();
        stream.setMode(XStream.NO_REFERENCES);
        stream.autodetectAnnotations(true);
        stream.alias("Item1", Item1.class);

        System.out.println(stream.toXML(t1));
    }
}

抽象项目.java

import com.thoughtworks.xstream.annotations.XStreamAsAttribute;

public abstract class AbstractItem {
    @XStreamAsAttribute
    public String name;
}

项目1.java

import com.thoughtworks.xstream.annotations.XStreamAsAttribute;

public class Item1 extends AbstractItem {
    @XStreamAsAttribute
    public String description;
}


更新: 我试图使用转换器类来做到这一点,但它仍然不正确:

stream.registerConverter(
        new Converter(){

            @Override
            public boolean canConvert(Class type) {
                if (AbstractItem.class.isAssignableFrom(type)){
                    return true;
                }
                return false;
            }

            @Override
            public void marshal(Object source, HierarchicalStreamWriter writer,
                    MarshallingContext context) {
                AbstractItem item = (AbstractItem)source;
                if(source instanceof Item1){
                    writer.startNode("Item1");
                    writer.addAttribute("description",((Item1)item).description);
                } else if(source instanceof Item2){
                    writer.startNode("Item2");
                    writer.addAttribute("description", ((Item2)item).description);
                } else {
                    writer.startNode("Item");
                }
                writer.addAttribute("name", item.name);
                writer.endNode();
            }

            @Override
            public Object unmarshal(HierarchicalStreamReader reader,
                    UnmarshallingContext context) {
                // TODO Auto-generated method stub
                AbstractItem item = null;
                String nodeName = reader.getNodeName();
                if (nodeName.equals("Item1")){
                    item = new Item1();
                    ((Item1)item).description = reader.getAttribute("description");
                } else if (nodeName.equals("Item2")){
                    item = new Item2();
                    ((Item2)item).description = reader.getAttribute("description");
                }  
                item.name = reader.getAttribute("name");
                return item;
            }
        });

我现在得到的结果是:

<Test>
  <item class="Item1">
    <Item1 description="description 1" name="name 1"/>
  </item>
</Test>
4

2 回答 2

1

我发现实现这一点的唯一方法是使用包含我要为其操作元素标记的对象的类的自定义转换器。在问题的示例中,它将是Test该类的自定义转换器,它看起来像:

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class BasicConverter implements Converter {

    @Override
    public boolean canConvert(Class type) {
        return Test.class.isAssignableFrom(type);
    }

    @Override
    public void marshal(Object source, HierarchicalStreamWriter writer,
            MarshallingContext context) {
        if (((Test) source).item instanceof Item1) {
            writer.startNode("Item1");
            writer.addAttribute("description", ((Item1)((Test) source).item).description);
        } else if (((Test) source).item instanceof Item2) {
            writer.startNode("Item2");
            writer.addAttribute("description", ((Item2)((Test) source).item).description);
        }
        writer.addAttribute("name", ((Test) source).item.name);
        writer.endNode();
    }

    @Override
    public Object unmarshal(HierarchicalStreamReader reader,
            UnmarshallingContext context) {
        Test test = new Test();

        reader.moveDown();
        String nodeName = reader.getNodeName();
        AbstractItem item = null;       
        if (nodeName.equals("Item1")) {
            item = new Item1();
            ((Item1)item).description = reader.getAttribute("description");
        } else if (nodeName.equals("Item2")) {
            item = new Item2();
            ((Item2)item).description = reader.getAttribute("description");
        }   
        item.name = reader.getAttribute("name");    
        ((Test)test).item = item;
        reader.moveUp();
        return test;
    }
}

这给出了我在上面寻找的输出,但对我来说并不令人满意。原因是,我需要使用它的实际类有大量字段,其中一些使用自己的自定义转换器、自定义别名等。此外,它基本上会忽略 Test 类上的所有注释,除了那些定义在班级水平。另外,随着班级的增长,您必须更新此转换器以处理这些新字段,否则它们将被包括在内。

理想情况下,我想要一个转换器,它可以执行注释定义的所有操作,但某些字段除外。目前没有我知道的。我正在做的是扩展com.thoughtworks.xstream.converters.reflection.ReflectionConverter课程以实现这一目标。但它需要从我特别关心的底层实现中复制更多代码。

于 2013-02-05T21:26:21.673 回答
0

XStream 我也面临同样的问题。我以前使用过使用反射来读取 xml 的Commons 消化器。这将为您提供读取 xml 时请求的功能。缺点是它无法编写 xml。我更喜欢消化器,因为不需要更改 java 类中的任何内容(没有注释或转换器的东西),并且很容易设置解析 xml 的规则。但话又说回来,它没有您从 XStream 或 Jaxb 获得的所有功能。现在我必须使用 XStream(或 Jaxb)来解决这个问题,所以我想我必须编写所需的转换器内容。谢谢你的例子。

于 2013-10-25T06:56:25.470 回答