When unmarshaling an XML string to a Java object, we get the “No default constructor found on class” error if a Java class does have a public no-args constructor. As discussed in this tutorial, we can solve this error either using the @XmlTransient annotation or using the XmlTypeAdapter class.
1. The “No default constructor found” Error
For demo purposes, let’s first create an example that will throw the error. Later we will solve the error with the suggested solutions.
In this example, we have two classes: Address and PinCode. The PinCode class has a constructor that accepts the pincode value. This means the compiler will generate a default constructor for this class.
@Data
@NoArgsConstructor
@AllArgsConstructor
@XmlRootElement(name = "Address")
@XmlAccessorType(XmlAccessType.FIELD)
class Address {
String line1;
String line2;
String city;
String country;
PinCode pinCode;
}
@Data
class PinCode {
String code;
public PinCode(String code){ // The default constructor will not be generated
this.code = code;
}
}
Now, we have XML data that we have to unmarshal:
String xmlAddress = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Address>
<line1>Line 1</line1>
<line2>Line 2</line2>
<city>New Delhi</city>
<country>India</country>
<pinCode>
<code>110075</code>
</pinCode>
</Address>
""";
Let’s try to unmarshal the XML string into the Address object using the JAXB Unmarshaller class.
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Address newAddress = (Address) jaxbUnmarshaller.unmarshal(new StringReader(xmlAddress));
As the PinCode class does not have a default no-args constructor, JAXB will not be able to create its instance in runtime and will throw this error:
Apr 16, 2024 1:28:15 PM org.glassfish.jaxb.core.v2.ClassFactory tryGetDeclaredConstructor
INFO: No default constructor found on class com.howtodoinjava.jaxb.PinCode
java.lang.NoSuchMethodException: com.howtodoinjava.jaxb.PinCode.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3761)
at java.base/java.lang.Class.getDeclaredConstructor(Class.java:2930)
Let us solve this error now.
2. Using @XmlTransient Annotation
The @XmlTransient annotation prevents mapping a JavaBean property/type to XML representation. The annotated field will be skipped from the marshalling and unmarshalling process.
For example, we can place the @XmlTransient annotation on the pinCode field in the Address class, as follows:
//...
class Address {
//...
@XmlTransient
PinCode pinCode;
}
Now, when we execute the previous unmarshalling code again we do not get any error. We will be able to unmarshal the Address object successfully. The value for PinCode will be null in the generated Address object.
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Address newAddress = (Address) jaxbUnmarshaller.unmarshal(new StringReader(xmlAddress));
System.out.println(newAddress);
The program output:
Address(line1=Line 1, line2=Line 2, city=New Delhi, country=India, pinCode=null)
3. Using XmlJavaTypeAdapter and XmlAdapter Classes
The XmlAdapter enables a Java type for custom marshaling. This is especially useful in situations where the class is not a default constructor or the field values need special formatting or parsing such as date fields or Map key-value pairs.
When applied to a field or class, during unmarshalling, the JAXB framework first unmarshals XML representation to a value type and then invokes XmlAdapter.unmarshal(..) to adapt the value type to a bound type.
In our usecase, we need to create a new class that is identical to the PinCode class and is compatible with the JAXB framework. For this, we add a default constructor to it. Let’s call this class XmlPinCode.
@Data
class XmlPinCode {
String code;
public XmlPinCode(){ // Added a default no-args constructor
}
public XmlPinCode(String code){
this.code = code;
}
}
Next, we create PinCodeAdapter class that species the mapping between the XmlPinCode and PinCode classes. Its marshal() and unmarshal() methods define the logic to create an instance from another instance.
class PinCodeAdapter extends XmlAdapter<XmlPinCode, PinCode> {
@Override
public PinCode unmarshal(XmlPinCode v) throws Exception {
return new PinCode(v.getCode());
}
@Override
public XmlPinCode marshal(PinCode v) throws Exception {
return new XmlPinCode(v.getCode());
}
}
Next, we add the @XmlJavaTypeAdapter(PinCodeAdapter.class) declaration on the PinCode field in the Address class.
//...
class Address {
//...
@XmlJavaTypeAdapter(PinCodeAdapter.class)
PinCode pinCode;
}
Now when we unmarshal an XML String to Address object, the pincode information is first populated into XmlPinCode object and later it is copied into PinCode object using the PinCodeAdapter.unmarshal() method.
Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
Address newAddress = (Address) jaxbUnmarshaller.unmarshal(new StringReader(xmlAddress));
System.out.println(newAddress);
The program output:
Address(line1=Line 1, line2=Line 2, city=New Delhi, country=India, pinCode=PinCode(code=110075))
4. Conclusion
This JAXB tutorial discussed how to solve the error “No default constructor found on class” when unmarshaling an XML string to Java POJO and the POJO does not contain a default no-argument constructor. We discussed solving this error using the @XmlTransient and @XmlJavaTypeAdapter annotations with examples.
Happy Learning !!
Comments