Неустрашимые коллекции в JaxB - PullRequest
14 голосов
/ 23 июня 2009

предположим, у меня есть этот класс:

public class A {

    private HashMap<String, B> map;

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

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

}

При попытке демонтировать XML-документ в этот класс с использованием JaxB, я замечаю, что вместо вызова метода setB () и отправки мне списка экземпляров B JaxB фактически вызывает getB () и добавляет экземпляры B в возвращаемый список , Почему?

Причина, по которой я хочу вызывать установщик, состоит в том, что список на самом деле является просто временным хранилищем, из которого я хочу построить поле карты, поэтому я подумал сделать это в установщике.

Спасибо.

Ответы [ 7 ]

8 голосов
/ 23 июня 2009

это способ, которым jaxb обрабатывает коллекции. вы должны быть уверены, что у вас есть ненулевая коллекция, когда jaxb пытается демонтировать.

есть плагин (никогда не использовал его сам), но может быть полезным: https://jaxb2 -commons.dev.java.net / коллекция сеттер-инжектор /

6 голосов
/ 06 января 2010

Hy,

вы можете использовать его с jaxb, это работает !!! (с мавеном ....)

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

и вы получите сеттер для своей коллекции

Надеюсь, это поможет людям

прощай

5 голосов
/ 09 марта 2012

Примечание: Я EclipseLink JAXB (MOXy) и являюсь членом JAXB 2 (JSR-222 ) экспертная группа.

Поведение, которое вы видите, будет различным в разных реализациях JAXB. Если вы не инициализируете значение для свойства List, EclipseLink JAXB (MOXy) будет вызывать метод set, как вы ожидаете.

Для получения дополнительной информации

<Ч />

Пример

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 {

}

Демо

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>

выход

Called setB
forum1032152.B@8bdcd2
forum1032152.B@4e79f1
3 голосов
/ 23 июня 2009

JAXB имеет проблемы с поддержкой интерфейсов и абстрактных классов; обычно он не знает, какой подкласс создать. Проблема в том, что обычным явлением является наличие класса в соответствии с:

ArrayList list;

@XMLElement
public List getList() {return this.list;}

Чтобы обойти это, JAXB даже не пытается создать экземпляр класса свойств (например, List), полученного из пары getter / setter, если это Collection. Он просто предполагает, что он не равен нулю и не может быть изменен.

Вероятно, самый простой способ обойти это - пометить ваш бизнес-интерфейс с помощью @XMLTransient и добавить другую пару геттер / установщик с @XMLElement для представления данных, которые вы хотите представить в JAXB. Я обычно делаю их защищенными, а не публичными, потому что не хочу, чтобы поведение JAXB было несколько глупым как часть публичного контракта моих классов.

1 голос
/ 12 мая 2010

Вы можете просто использовать массив вместо List)

1 голос
/ 13 апреля 2010

Jaxb2 UnMarshaller определяет интерфейс слушателя, который вызывается каждый раз, когда объект не маршалируется. Вы можете определить пользовательский слушатель для вызова методов установки во всех коллекциях (или на подобъектах). Это должно быть довольно легко сделать с любым из классов утилит bean. Я ищу существующую реализацию, хотя не вижу ее.

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

Если вы используете каркас пружины, это довольно просто:

    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);
                             }
                         }
                 }
             }
         }
    }
0 голосов
/ 09 августа 2012
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.

...