14

假设我有这个类:

public class A {

    private HashMap<String, B> map;

    @XmlElement
    private void setB(ArrayList<B> col) {
        ...
    }

    private ArrayList<B> getB() {
        ...
    }

}

当尝试使用 JaxB 将 xml 文档解组到此类时,我注意到 JaxB 并没有调用 setB() 方法并向我发送 B 实例列表,而是实际上调用了 getB() 并将 B 实例添加到返回的列表中。为什么?

我希望调用 setter 的原因是该列表实际上只是一个临时存储,我想从中构建 map 字段,所以我想在 setter 中进行。

谢谢。

4

7 回答 7

8

这就是 jaxb 处理集合的方式。当 jaxb 尝试解组时,您必须确保您有一个非空集合。

有一个插件(我自己从未使用过)但可能会有所帮助: https ://jaxb2-commons.dev.java.net/collection-setter-injector/

于 2009-06-23T12:46:53.130 回答
6

嗨,

您可以将它与 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>

你得到了你的集合的二传手

希望它会帮助人们

再见

于 2010-01-06T17:33:59.820 回答
5

注意: 我是EclipseLink JAXB (MOXy)负责人,也是JAXB 2 (JSR-222)专家组的成员。

您看到的行为会因 JAXB 实现而异。如果您不初始化该List属性的值,那么 EclipseLink JAXB (MOXy) 将按照您的预期调用 set 方法。

了解更多信息


例子

一个

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;
    }

}

package forum1032152;

public class B {

}

演示

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

}

输入.xml

<?xml version="1.0" encoding="UTF-8"?>
<a>
    <b></b>
    <b></b>
</a>

输出

Called setB
forum1032152.B@8bdcd2
forum1032152.B@4e79f1
于 2012-03-08T21:42:07.887 回答
3

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:

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.

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.

于 2009-06-23T18:47:35.950 回答
1

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());
    }
  });

如果您使用的是 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);
                             }
                         }
                 }
             }
         }
    }
于 2010-04-12T20:36:24.383 回答
1

您可以只使用数组而不是 List )

于 2010-05-12T03:57:56.133 回答
0
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 可以直接处理地图,因此,这可能会使对 setB() 的调用成为一个有争议的问题。如果这对您来说是一个可接受的解决方案,请参阅我在博客上维护的示例,以在 JAXB 中为地图创建适配器。

于 2012-08-08T23:14:06.243 回答