Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB and constructors

I'm starting learning JAXB, so my question can be very silly. Now I have classes and want generate XML Schema. Going after this instruction I get exception

IllegalAnnotationExceptions ... does not have a no-arg default constructor.

Yeah. My classes haven't default no-arg constructors. It's too easy. I have classes with package visible constructors / final methods and off course with arguments. What shall I do - create some specific momemto/builder classes or specify my constructors to JAXB (in what way?) ? Thanks.

like image 744
Stan Kurilin Avatar asked Dec 08 '10 12:12

Stan Kurilin


People also ask

Is JAXB deprecated?

In Java SE 9 and 10, the module java.se.ee, which contains JAX-WS, JAXB and a few other Java EE APIs, is still included but is deprecated, and not included in the module path by default. You need to explicitly enable it if you want to use it.

What can I use instead of JAXB?

XOM, JDOM, dom4j, etc. etc. Projects like Castor and Apache XMLBeans predate JAXB, so you could have a look at those. Ulf Dittmer wrote: XOM, JDOM, dom4j, etc.

What is JAXB used for?

JAXB simplifies access to an XML document from a Java program by presenting the XML document to the program in a Java format. The first step in this process is to bind the schema for the XML document into a set of Java classes that represents the schema.

What constructor is required by all objects that are to be marshalled Unmarshalled by Jackson?

The existence of a no-arg constructor gets validated during creation of the JAXBContext and therefore applies regardless if you want to use JAXB for marshalling or unmarshalling.


2 Answers

JAXB can support this case using an XML Adapter. Consider you have the following object with no zero-arg constructor:

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

You simply need to create a mappable version of this class:

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

And an XML Adapter to convert between them:

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

Then for properties that refer to the Customer class, simply use the @XmlJavaTypeAdapter annotation:

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

For a more detailed example see:

  • http://bdoughan.blogspot.com/2010/12/jaxb-and-immutable-objects.html
like image 89
bdoughan Avatar answered Sep 24 '22 09:09

bdoughan


You can use the annotation @XmlType and use factoryMethod / factoryClass attributes in various combinations such as:

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

Surprisingly this works and you get an initialized instance when unmarshalling. You should make note not to call the newInstance method anywhere on your code as it will return an invalid instance.

like image 34
Rafael M Avatar answered Sep 26 '22 09:09

Rafael M