2010-12-08 18 views
28

Estoy comenzando a aprender JAXB, por lo que mi pregunta puede ser muy tonta. Ahora tengo clases y quiero generar XML Schema. Va después de la instrucción this consigo excepciónJAXB y constructores

IllegalAnnotationExceptions ... no tiene un no-arg predeterminado constructor.

Sí. Mis clases no tienen constructores predeterminados de no-arg. Es demasiado fácil. Tengo clases con constructores/métodos finales visibles del paquete y fuera de curso con argumentos. ¿Qué debo hacer? Crear algunas clases específicas de momemto/builder o especificar mis constructores a JAXB (¿de qué manera?)? Gracias.

Respuesta

42

JAXB puede admitir este caso utilizando un adaptador XML. Considere usted tiene el siguiente objeto con ningún constructor cero-arg:

package blog.immutable; 

public class Customer { 

    private final String name; 
    private final Address address; 

    public Customer(String name, Address address) { 
     this.name = name; 
     this.address = address; 
    } 

    public String getName() { 
     return name; 
    } 

    public Address getAddress() { 
     return address; 
    } 

} 

Usted sólo tendrá que crear una versión mapeable de esta clase:

package blog.immutable.adpater; 

import javax.xml.bind.annotation.XmlAttribute; 
import blog.immutable.Address; 

public class AdaptedCustomer { 

    private String name; 
    private Address address; 

    @XmlAttribute 
    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public Address getAddress() { 
     return address; 
    } 

    public void setAddress(Address address) { 
     this.address = address; 
    } 

} 

y un adaptador de XML para convertir entre ellos:

package blog.immutable.adpater; 

import javax.xml.bind.annotation.adapters.XmlAdapter; 
import blog.immutable.Customer; 

public class CustomerAdapter extends XmlAdapter<AdaptedCustomer, Customer> { 

    @Override 
    public Customer unmarshal(AdaptedCustomer adaptedCustomer) throws Exception { 
     return new Customer(adaptedCustomer.getName(), adaptedCustomer.getAddress()); 
    } 

    @Override 
    public AdaptedCustomer marshal(Customer customer) throws Exception { 
     AdaptedCustomer adaptedCustomer = new AdaptedCustomer(); 
     adaptedCustomer.setName(customer.getName()); 
     adaptedCustomer.setAddress(customer.getAddress()); 
     return adaptedCustomer; 
    } 

} 

continuación para las propiedades que se refiere a la clase de cliente, basta con utilizar la anotación @XmlJavaTypeAdapter:

package blog.immutable; 

import javax.xml.bind.annotation.XmlRootElement; 
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 
import blog.immutable.adpater.CustomerAdapter; 

@XmlRootElement(name="purchase-order") 
public class PurchaseOrder { 

    private Customer customer; 

    @XmlJavaTypeAdapter(CustomerAdapter.class) 
    public Customer getCustomer() { 
     return customer; 
    } 

    public void setCustomer(Customer customer) { 
     this.customer = customer; 
    } 

} 

Para un ejemplo más detallado, véase:

+3

por favor no copie los enlaces directamente, explique los conceptos básicos en su respuesta, y eventualmente enlace a la publicación del blog si se necesita más explicación. De esta forma, si el enlace cae muerto, todavía tendremos la respuesta. –

+1

Esto es lo que necesito gracias. –

+2

Estoy de acuerdo no solo con copiar enlaces. Esta respuesta se publicó durante mi viaje al trabajo desde mi teléfono. Proporcionaré más detalles cuando estoy en una computadora. El enlace es a mi propio blog si eso ayuda –

5

Debe tener un constructor predeterminado para que JAXB pueda crear instancias de sus clases. Tal vez hay una solución que no sé.

JAXB está especialmente diseñado para clases similares a beans, lo que permite configurar objetos al invocar setters sobre ellos.

+1

Crear un constructor predeterminado para todas las clases es una muy mala idea. Debe haber alguna solución. –

+0

No me gusta ese diseño tampoco, pero esta es la forma estándar JAXB ... – Guillaume

+0

Francamente, realmente no entiendo el racismo sin argumentos.Sus clases de bean solo deberían ser contenedores de información, que pueden transformarse fácilmente en XML, no clases con inicialización compleja. –

3

JAXB vuelve a crear beans de XML de una manera simple: crea una nueva instancia del bean, y luego hace todo el setXXX necesario para establecer los atributos. Entonces, si su bean no tiene un constructor sin argumentos, JAXB no puede crearlo. Como se dijo en otras respuestas, JAXB funciona mejor para beans "contenedor" simples, para los cuales el constructor no-args no es realmente un problema. Si intenta crear beans que necesitan una inicialización específica, deberá hacerlo en los métodos setXXX.

+2

Creo que debe haber algo de práctica para trabajar con objetos inmutables y así sucesivamente .. –

+0

@Stas: vea mi comentario sobre la respuesta de Guillaume. Si JAXB tiene que crearlo, no puede ser inmutable. JAXB no está hecho para crear objetos inmutables, sino más objetos de "transporte" de los que tomas información más adelante. –

+0

O para decirlo de forma simple, si no tiene un constructor público no-args, no es un bean. Eso es parte de la especificación javabean. –

13

Puede utilizar la anotación @XmlType y utilizar los atributos factoryMethod/factoryClass en varias combinaciones, tales como:

@XmlType(factoryMethod="newInstance") 
@XmlRootElement 
public class PurchaseOrder { 
    @XmlElement 
    private final String address; 
    @XmlElement 
    private final Customer customer; 

    public PurchaseOrder(String address, Customer customer){ 
     this.address = address; 
     this.customer = customer; 
    } 

    private PurchaseOrder(){ 
     this.address = null; 
     this.customer = null; 
    } 
    /** Creates a new instance, will only be used by Jaxb. */ 
    public static PurchaseOrder newInstance() { 
     return new PurchaseOrder(); 
    } 

    public String getAddress() { 
     return address; 
    } 

    public Customer getCustomer() { 
     return customer; 
    } 
} 

Sorprendentemente, esto funciona d obtienes una instancia inicializada al desasociar. Debe tomar nota de no llamar al método newInstance en ningún lugar de su código, ya que devolverá una instancia no válida.

+0

Es realmente genial. Debería intentarlo. –

+3

La creación de un método público lo hará accesible para cualquier persona, no solo para JAXB, lo que podría socavar su diseño y anular y restringir los campos introducidos en el momento del diseño. No puedo recomendar esta solución! – Eric

+0

Sí, estoy al tanto de eso, pero como siempre depende de lo que está dispuesto a conceder en aras de la simplicidad. Podría jugar a lo largo de la definición de interfaces para todos sus métodos, ocultando así cualquier otro método de utilidad, pero una vez más, no estoy seguro de lo que el póster original quería resolver. –