问题描述
|
我在一个用@XmlElementWrapper(name = \“ foos \”)和@XmlElement(name = \“ foo \”)注释的类上有一个列表设置器。
当我解组没有 foos>或 元素的XML时,将调用setter并传递一个空列表。有没有办法得到以下结果?
如果没有 ,请不要调用设置器。或者,如果必须调用setter,则传递null。
当 存在但为空时,将一个空列表传递给setter。
当具有一个或多个子 元素时,传递一个填充列表。
解决方法
您可以在此用例中使用XmlAdapter:
input1.xml
没有的时候,不要打电话
塞特犬。或者如果二传手必须是
调用,传递null。
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<root>
<child/>
</root>
input2.xml
当存在但空着时,
将空列表传递给设置者。
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<root>
<child>
<foos/>
</child>
</root>
input3.xml
什么时候有一个或多个孩子
元素,通过填充
清单。
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<root>
<child>
<foos>
<foo>Hello World</foo>
</foos>
</child>
</root>
根
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlRootElement
public class Root {
private Child child;
@XmlJavaTypeAdapter(ChildAdapter.class)
public Child getChild() {
return child;
}
public void setChild(Child child) {
this.child = child;
}
}
儿童
import java.util.List;
public class Child {
private List<String> strings;
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
System.out.println(\"setStrings\");
this.strings = strings;
}
}
子适配器
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ChildAdapter extends XmlAdapter<ChildAdapter.AdaptedChild,Child> {
public static class AdaptedChild {
public Foos foos;
}
public static class Foos {
public List<String> foo;
}
@Override
public Child unmarshal(AdaptedChild adaptedChild) throws Exception {
Child child = new Child();
Foos foos = adaptedChild.foos;
if(null != foos) {
List<String> foo = foos.foo;
if(null == foo) {
child.setStrings(new ArrayList<String>());
} else {
child.setStrings(foos.foo);
}
}
return child;
}
@Override
public AdaptedChild marshal(Child child) throws Exception {
AdaptedChild adaptedChild = new AdaptedChild();
List<String> strings = child.getStrings();
if(null != strings) {
Foos foos = new Foos();
foos.foo = strings;
adaptedChild.foos = foos;
}
return adaptedChild;
}
}
演示版
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,true);
Object o;
o = unmarshaller.unmarshal(new File(\"input1.xml\"));
marshaller.marshal(o,System.out);
o = unmarshaller.unmarshal(new File(\"input2.xml\"));
marshaller.marshal(o,System.out);
o = unmarshaller.unmarshal(new File(\"input3.xml\"));
marshaller.marshal(o,System.out);
}
}
输出量
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<root>
<child/>
</root>
setStrings
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<root>
<child>
<foos/>
</child>
</root>
setStrings
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<root>
<child>
<foos>
<foo>Hello World</foo>
</foos>
</child>
</root>
,这是适配器,最终可以解决Blaise Doughan的答案的评论中提到的复杂问题:
public class ListOfFooAdapter extends XmlAdapter<ListOfFooAdapter.Adapted,List<Foo>> {
@XmlRootElement(name = \"foos\")
public static class Adapted {
public List<Foo> foo;
}
@Override
public List<Foo> unmarshal(Adapted adapted) throws Exception {
return adapted.foo;
}
@Override
public Adapted marshal(List<Foo> foo) throws Exception {
if (null == foo) {
return null;
} else {
Adapted adapted = new Adapted();
adapted.foo = foo;
return adapted;
}
}
}
...除非XML中存在元素,否则不会调用unmarshall方法。
我这样注释了列表属性:
@XmlJavaTypeAdapter(ListOfFooAdapter.class)
public List<Foo> getFoos() {
...
}
public void setFoos(List<Foo> l) {
...
}
,我试图解决一个相同的问题,并在这里玩了一段时间。非常感谢他们,非常有教育意义。
但是,似乎不正确的事情是,像JDK 6中的JAXB实现一样成熟的东西,可以理解XML中的元素,并刻意让我编写一个空列表。
事实证明,JAXB在调用我的setter后引用了仍然为空的列表,从而填充了列表,因此当我的setter代码看起来像这样时,它在其余的编组阶段中错过了对列表的后续更新:
@XmlElementWrapper(name = \"foos\")
@XmlElement(name = \"foo\")
public void setFoos(List<Foo> newFoos) {
this.foos.clear();
this.foos.addAll(newFoos);
}
当我将设置器修改为
@XmlElementWrapper(name = \"foos\")
@XmlElement(name = \"foo\")
public void setFoos(List<Foo> newFoos) {
this.foos = newFoos;
}
它工作得很好。挂在别人对列表的指针上总是一种烦躁的经历,因为列表所有者可以在事后修改它,但是在这种情况下,正是这种更改才解决了问题。
我通过遍历JAXB传递的列表来确认我的假设,并确定它抱怨ConcurrentModificationException,这证明在将其交给我的代码后它确实在列表上起作用。
,您可以将所有逻辑放在beforeUnmarshall
,afterUnmarshall
方法或Unmarshaller
侦听器中,并在解组完成后执行。
有关更多信息,请参见https://stackoverflow.com/a/4378648/751200。