根据Abstract类值的具体实现更改Element标签

时间:2013-02-04 21:03:47

标签: java xstream

假设我有一个名为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);

上述任何一项都没有奏效。

<小时/> 为了清楚起见,这里是一个可运行的例子:

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

AbstractItem.java

import com.thoughtworks.xstream.annotations.XStreamAsAttribute;

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

Item1.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>

2 个答案:

答案 0 :(得分:1)

我发现实现这一目标的唯一方法是使用包含我想要操作元素标记的Object的类的自定义转换器。在问题的示例中,它将是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类来实现这一目标。但它需要从底层实现复制更多代码,然后我特别关心。

答案 1 :(得分:0)

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