JAXB y constructores


Estoy empezando a aprender JAXB, así que mi pregunta puede ser muy tonta. Ahora tengo clases y quiero generar esquema XML. Yendo después de esta instrucción obtengo la excepción

Excepciones a la anotación ilegal ... no tiene un valor predeterminado no-arg constructor.

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

Author: Stan Kurilin, 2010-12-08

4 answers

JAXB puede soportar este caso usando un adaptador XML. Considere que tiene el siguiente objeto sin constructor zero-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;
    }

}

Simplemente necesita crear una versión asignable 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 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;
    }

}

Luego, para las propiedades que se refieren a la clase Customer, simplemente use 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:

 42
Author: Blaise Doughan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 14:06:41

Puede usar la anotación @XmlType y usar 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. */
    private static PurchaseOrder newInstance() {
        return new PurchaseOrder();
    }

    public String getAddress() {
        return address;
    }

    public Customer getCustomer() {
        return customer;
    }
}

Sorprendentemente esto funciona y se obtiene una instancia inicializada cuando se desmarca. 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.

 14
Author: Rafael M,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-15 18:21:03

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

JAXB está especialmente adaptado para clases tipo bean, permitiendo configurar objetos llamando a setters sobre ellos.

 5
Author: Guillaume,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 12:26:48

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

 3
Author: Valentin Rocher,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-08 12:30:57