2009-06-23 19 views
14

supongamos que tengo esta clase:colecciones unmarshalling en jaxb

public class A { 

    private HashMap<String, B> map; 

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

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

} 

Al intentar Resolver referencia un documento XML a esta clase mediante jaxb Me he dado cuenta que en lugar de llamar al método SETB() y me envía la lista de B instancias JaxB realmente llama al getB() y agrega las instancias B a la lista devuelta. ¿Por qué?

La razón por la que quiero que se llame al instalador es porque la lista es en realidad solo un almacenamiento temporal desde el que quiero construir el campo del mapa, así que pensé en hacerlo en el colocador.

Gracias.

Respuesta

8

esa es la forma en que jaxb maneja las colecciones. debes asegurarte de tener una colección no nula cuando jaxb intente desempatar.

hay un plugin (nunca utilizado yo mismo), pero puede ser útil: https://jaxb2-commons.dev.java.net/collection-setter-injector/

+0

+1 para señalar la forma en que jaxb hace colecciones, pero ese complemento no ayudará. Es un complemento para el generador de códigos XJC, my no cambia el comportamiento en tiempo de ejecución de JAXB. – skaffman

+0

Leí un hilo viejo de correo electrónico que decía que este comportamiento se estaba solucionando en JaxB 2.1. Esperaría que haya una propiedad en JaxbContext.newInstance (classes, * properties *) que controle el comportamiento de cálculo de referencias, pero no puedo encontrarlo. – Justin

3

JAXB tiene problemas de compatibilidad con interfaces y clases abstractas; por lo general, no sabe qué subclase para instanciar. El problema es que es un patrón común tener una clase a lo largo de las líneas de:

ArrayList list; 

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

Para evitar esto, JAXB ni siquiera intenta crear una instancia de la clase de resistencia (por ejemplo, lista) derivado del captador/definidor par si es una Colección. Simplemente asume que es no nulo y modificable.

Probablemente el trabajo más simple sea marcar su interfaz de negocio con @XMLTransient y agregar un par getter/setter diferente con @XMLElement para la vista de los datos que desea exponer a JAXB. Por lo general, los hago más protegidos que públicos, porque no me importa tener el comportamiento un tanto ridículo de JAXB como parte del contrato público de mis clases.

6

Hy,

se puede usar con jaxb, es un trabajo !!! (Con 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> 

y le entregan su colocador para su colección

esperanza de que ayudaría a la gente

bye

+0

para elegir el paquete, también puede agregar el parámetro com.your.package George

1

Jaxb2 unmarshaller define una interfaz de receptor que se llama cada vez que un objeto ha sido descalificado. Puede definir un oyente personalizado para invocar métodos setter en todas las colecciones (o en subobjetos). Esto debería ser bastante fácil de hacer con cualquiera de las clases de bean utils. Estoy buscando una implementación existente, aunque no veo ninguna.

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

Si está utilizando el marco de primavera, es bastante sencillo:

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

Usted sólo puede utilizar una matriz en lugar de Lista)

5

Nota: Soy el plomo EclipseLink JAXB (MOXy) y un miembro del grupo de expertos JAXB 2 (JSR-222).

El comportamiento que está viendo variará entre las implementaciones de JAXB. Si no inicializa un valor para la propiedad List, entonces EclipseLink JAXB (MOXy) llamará al método set como esperaba.

Para más información


Ejemplo

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 { 

} 

Demostración

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> 

salida

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 puede manejar directamente los mapas, por lo tanto, esto podría hacer la llamada a SETB() en un punto discutible. Si esa es una solución aceptable para usted, consulte el example que mantengo en mi blog para crear un adaptador para mapas en JAXB.

Cuestiones relacionadas