2009-06-23 7 views
14

Предположим, у меня есть этот класс:немаршалинг коллекции в JAXB

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 в возвращенный список. Зачем?

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

Спасибо.

ответ

8

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

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

+0

+1 для указания способа, которым jaxb делает коллекции, но этот плагин не поможет. Это плагин для генератора кода XJC, m и не изменяет поведение среды JAXB. – skaffman

+0

Я прочитал старую электронную почту, в которой утверждалось, что это поведение исправлено в JaxB 2.1. Я ожидал бы, что есть свойство, которое на JaxbContext.newInstance (classes, * properties *), которое управляет маршалингом, но я не могу его найти. – Justin

3

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

ArrayList list; 

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

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

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

6

Hy,

вы можете использовать его с 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> 

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

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

распрощаться

+0

, чтобы выбрать пакет, вы также можете добавить параметр com.your.package George

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

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

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

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

5

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

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

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


ПРИМЕР

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

} 

Input.xml

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

Выход

Called setB 
[email protected] 
[email protected] 
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() спорный пункт. Если это приемлемое решение для вас, см. Раздел example, который я поддерживаю в своем блоге, чтобы создать адаптер для карт в JAXB.