java 在 JaxB 中解组集合
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1032152/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Unmarshalling collections in JaxB
提问by Stas
suppose I have this class:
假设我有这门课:
public class A {
private HashMap<String, B> map;
@XmlElement
private void setB(ArrayList<B> col) {
...
}
private ArrayList<B> getB() {
...
}
}
When trying to unmarshall an xml document to this class using JaxB I notice that instead of calling the setB() method and sending me the list of B instances JaxB actually calls the getB() and adds the B instances to the returned list. Why?
当尝试使用 JaxB 将 xml 文档解组到此类时,我注意到不是调用 setB() 方法并向我发送 B 实例的列表,而是 JaxB 实际上调用了 getB() 并将 B 实例添加到返回的列表中。为什么?
The reason I want the setter to be called is that the list is actually just a temporary storage from which I want to build the map field, so I thought to do it in the setter.
我希望setter被调用的原因是列表实际上只是一个临时存储,我想从中构建map字段,所以我想在setter中进行。
Thanks.
谢谢。
回答by silmx
thats the way jaxb is handling collections. you have to be sure you have a non null collection when jaxb try to unmarshal.
这就是 jaxb 处理集合的方式。当 jaxb 尝试解组时,您必须确保您有一个非空集合。
there is a plugin (never used it myself) but can be helpful: https://jaxb2-commons.dev.java.net/collection-setter-injector/
有一个插件(我自己从未使用过)但可能会有所帮助:https: //jaxb2-commons.dev.java.net/collection-setter-injector/
回答by LE GALL Beno?t
Hy,
嗨,
you can use it with jaxb, it's work !!! (with Maven....)
您可以将它与 jaxb 一起使用,它的工作!(与 Maven ......)
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<args>
<arg>-Xcollection-setter-injector</arg>
</args>
<plugins>
<plugin>
<groupId>net.java.dev.vcc.thirdparty</groupId>
<artifactId>collection-setter-injector</artifactId>
<version>0.5.0-1</version>
</plugin>
</plugins>
<schemaDirectory>src/schemas</schemaDirectory>
<generateDirectory>src/main/java</generateDirectory>
<extension>true</extension>
</configuration>
</plugin>
and you get your setter for your Collection
然后你得到了你的收藏的二传手
Hope it would help people
希望它能帮助人们
bye
再见
回答by bdoughan
Note:I'm the EclipseLink JAXB (MOXy)lead and a member of the JAXB 2 (JSR-222)expert group.
注意:我是EclipseLink JAXB (MOXy) 的负责人和JAXB 2 (JSR-222)专家组的成员。
The behaviour you are seeing will vary among JAXB implementations. If you do not initialize a value for the Listproperty then EclipseLink JAXB (MOXy) will call the set method as you expect.
您看到的行为会因 JAXB 实现而异。如果您没有为该List属性初始化一个值,那么 EclipseLink JAXB (MOXy) 将按照您的预期调用 set 方法。
For More Information
想要查询更多的信息
- http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
- http://blog.bdoughan.com/2011/01/jaxb-and-choosing-list-implementation.html
- http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
- http://blog.bdoughan.com/2011/01/jaxb-and-choosing-list-implementation.html
EXAMPLE
例子
A
一个
package forum1032152;
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class A {
private ArrayList<B> b;
@XmlElement
public void setB(ArrayList<B> col) {
System.out.println("Called setB");
for(B b : col) {
System.out.println(b);
}
this.b = col;
}
public ArrayList<B> getB() {
return b;
}
}
B
乙
package forum1032152;
public class B {
}
Demo
演示
package forum1032152;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(A.class);
File xml = new File("src/forum1032152/input.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.unmarshal(xml);
}
}
input.xml
输入文件
<?xml version="1.0" encoding="UTF-8"?>
<a>
<b></b>
<b></b>
</a>
Output
输出
Called setB
forum1032152.B@8bdcd2
forum1032152.B@4e79f1
回答by gibbss
JAXB has problems supporting interfaces and abstract classes; it usually doesn't know what subclass to instantiate. The problem is, it's a common pattern to have a class along the lines of:
JAXB 存在支持接口和抽象类的问题;它通常不知道要实例化哪个子类。问题是,按照以下方式创建类是一种常见模式:
ArrayList list;
@XMLElement
public List getList() {return this.list;}
To get around this, JAXB doesn't even try to instantiate the property class (e.g. List) derived from the getter/setter pair if it's a Collection. It just assumes that it's non-null and modifiable.
为了解决这个问题,JAXB 甚至不会尝试实例化从 getter/setter 对派生的属性类(例如 List),如果它是一个 Collection。它只是假设它是非空且可修改的。
Probably the simplest work around is to mark your business interface with @XMLTransient and add a different getter/setter pair with @XMLElement for the view for the data that you want to expose to JAXB. I usually make these protected rather than public, because I don't care to have the somewhat-goofy JAXB behavior as part of my classes' public contract.
可能最简单的解决方法是使用 @XMLTransient 标记您的业务接口,并使用 @XMLElement 添加不同的 getter/setter 对,用于您要向 JAXB 公开的数据的视图。我通常将这些设置为 protected 而不是 public,因为我不关心将有点愚蠢的 JAXB 行为作为我的类的公共契约的一部分。
回答by Justin
Jaxb2 UnMarshaller defines a listener interface which is called any time an object has been un-marshaled. You can define a custom listener to invoke setter methods on all collections (or on sub-objects). This should be pretty easy to do with any one of the bean utils classes. I'm looking for an existing implementation, though I don't see one.
Jaxb2 UnMarshaller 定义了一个侦听器接口,只要对象被解组,就会调用该接口。您可以定义一个自定义侦听器来调用所有集合(或子对象)上的 setter 方法。使用任何一个 bean utils 类都应该很容易做到这一点。我正在寻找一个现有的实现,但我没有看到。
JAXBContext context = JAXBContext.newInstance( classesToBeBound );
m_unmarshaller = context.createUnmarshaller();
m_unmarshaller.setListener(
new Unmarshaller.Listener() {
public void afterUnmarshal(Object target, Object parent) {
for (Property p : getBeanProperties(target.getClass()))
if (p.isCollectionType() || p.isCompositeType())
p.invokeSetter(p.invokeGetter());
}
});
If you are using the spring framework, its pretty straightforward:
如果您使用的是 spring 框架,它非常简单:
new Unmarshaller.Listener() {
public void afterUnmarshal(Object target, Object parent) {
BeanWrapper wrapper = new BeanWrapperImpl(target);
for (PropertyDescriptor pd : wrapper.getPropertyDescriptors()) {
if (pd.getPropertyType() != null) {
if (!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
try {
Method setter = pd.getWriteMethod();
if (setter != null) {
Method getter = pd.getReadMethod();
if (getter != null)
setter.invoke(target, getter.invoke(target));
}
}
catch (Exception ex) {
s_logger.error("can't invoke setter", ex);
}
}
}
}
}
}
回答by alles
You can just use an array instead of List )
您可以只使用数组而不是 List )
回答by Jér?me Verstrynge
The reason I want the setter to be called is that the list is actually
just a temporary storage from which I want to build the map field,
so I thought to do it in the setter.
JAXB can handle maps directly, hence, this could make the call to setB() a moot point. If that is an acceptable solution for you, see the exampleI maintain on my blog to create an adaptor for maps in JAXB.
JAXB 可以直接处理映射,因此,这可能会使调用 setB() 成为一个有争议的问题。如果这是您可以接受的解决方案,请参阅我在博客中维护的示例,以在 JAXB 中为地图创建适配器。

