JAXB implementations (JSR-222) can represent null either as a missing node or as a null element based on setting nillable to @XmlElement . When you need to support both, or distinguish between the two, you can use JAXBElement .
Java Model
Root
Fields / properties of type JAXBElement displayed with the annotation @XmlElementRef . This corresponds to the @XmlElementDecl annotation for the class annotated with @XmlRegistry .
import java.math.BigDecimal; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; @XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) public class Root { @XmlElementRef(name="foo") JAXBElement<BigDecimal> foo; @XmlElementRef(name="bar") JAXBElement<BigDecimal> bar; @XmlElementRef(name="baz") JAXBElement<BigDecimal> baz; }
ObjectFactory
import java.math.BigDecimal; import javax.xml.bind.JAXBElement; import javax.xml.bind.annotation.*; import javax.xml.namespace.QName; @XmlRegistry public class ObjectFactory { @XmlElementDecl(name = "foo") public JAXBElement<BigDecimal> createFoo(BigDecimal value) { return new JAXBElement<BigDecimal>(new QName("foo"), BigDecimal.class, value); } @XmlElementDecl(name = "bar") public JAXBElement<BigDecimal> createBar(BigDecimal value) { return new JAXBElement<BigDecimal>(new QName("bar"), BigDecimal.class, value); } @XmlElementDecl(name = "baz") public JAXBElement<BigDecimal> createBaz(BigDecimal value) { return new JAXBElement<BigDecimal>(new QName("baz"), BigDecimal.class, value); } }
Demo code
Input.xml
<?xml version="1.0" encoding="UTF-8"?> <root> <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/> <baz>123.456</baz> </root>
Demo
Below is some demo code that you can run to show that everything is working. Note how the JAXBIntrospector can be used to get the actual value by JAXBElement if necessary.
import java.io.File; import java.math.BigDecimal; import javax.xml.bind.*; public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Root.class, ObjectFactory.class); Unmarshaller unmarshaller = jc.createUnmarshaller(); File xml = new File("src/forum18440987/input.xml"); Root root = (Root) unmarshaller.unmarshal(xml); nullOrAbsent("foo", root.foo); nullOrAbsent("bar", root.bar); nullOrAbsent("baz", root.baz); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(root, System.out); } private static void nullOrAbsent(String property, JAXBElement<BigDecimal> value) { System.out.print(property); if(null == value) { System.out.print(": ABSENT - "); } else if(value.isNil()) { System.out.print(": NIL - "); } else { System.out.print(": VALUE - "); } System.out.println(JAXBIntrospector.getValue(value)); } }
Output
foo: ABSENT - null bar: NIL - null baz: VALUE - 123.456 <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <root> <bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/> <baz>123.456</baz> </root>
UPDATE
If you want to keep the existing get / set methods, you can save access to the fields, as I have in this answer, and change the access methods to look like this:
public BigDecimal getBar() { if(null == bar) { return null; } return bar.getValue(); } public void setBar(BigDecimal bar) { if(null == this.bar) { this.bar = new JAXBElement<BigDecimal>(new QName("bar"), BigDecimal.class, bar); } else { this.bar.setValue(bar); } }
Alternatively, you can add the isSet method to find out if a value has been set.
public boolean isSetBar() { return null != bar; }
This approach does not require you to have access to Unmarshaller . To make sure that the ObjectFactory selected, you can use the @XmlSeeAlso annotation to reference it from one of your domain classes.
@XmlRootElement @XmlAccessorType(XmlAccessType.FIELD) @XmlSeeAlso(ObjectFactory.class) public class Root {