假设我有一个名为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>
答案 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)来解决这个问题,所以我想我必须编写所需的转换器内容。谢谢你的例子。