Reputation: 15792
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.
Upvotes: 31
Views: 31637
Reputation: 393
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.
Upvotes: 16
Reputation: 149017
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:
Upvotes: 42
Reputation: 11669
JAXB re-creates beans from XML in a simple fashion : it creates a new instance of the bean, and then do all the setXXX
needed to set the attributes. So, if your bean doesn't have a no-args constructor, JAXB can't create it. As said in other answers, JAXB works better for simple "container" beans, for which no-args constructor isn't really a problem. If you're trying to create beans that need specific initialization, you'll need to do it in the setXXX
methods.
Upvotes: 3
Reputation: 5555
You should have a default constructor for JAXB to be able to instantiate your classes. Maybe there is a workaround I don't know though.
JAXB is especially fitted for bean-like classes, permitting to configure objects by calling setters on them.
Upvotes: 5