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.
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.
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.
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.
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.
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:
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With