JAXB generate nillable = "true" from java

user1414745 picture user1414745 · Nov 19, 2013 · Viewed 18k times · Source

it this a bug?

I need nillable = "true" in my xsd schema. The only way to generate such an element from my java code is to use @XmlElement(nillable = true), right? But in this case, I will not be able to take advantage of this definition, I will not be able to check if the element is set to nil. The method isNil() is in the JAXBElement wrapper class.

So, what are my options here - I want to generate nillable = "true" in my xsd schema AND be able to check if it is set from my java code.

Answer

bdoughan picture bdoughan · Nov 19, 2013

I need nillable = "true" in my xsd schema. The only way to generate such an element from my java code is to use @XmlElement(nillable = true), right?

Yes.

But in this case, I will not be able to take advantage of this definition, I will not be able to check if the element is set to nil. The method isNil() is in the JAXBElement wrapper class.

You can do getFoo() == null to determine if it was null. If you are trying to determine if the null corresponds to an absent element or xsi:nil="true" then you will have to do more. A set won't be done for absent nodes so you can put logic in your setter to track if a set to null was done because of an element with xsi:nil="true.

@XmlElement(nillable=true)
public String getFooString() {
    return fooString;
}

public void setFooString(String foo) {
    this.fooString = foo;
    this.setFoo = true;
}

If you don't want to have this extra logic (which doesn't help for marshalling anyways you need to leverage JAXBElement.

@XmlElementRef(name="fooJAXBElement")
public JAXBElement<String> getFooJAXBElement() {
    return fooJAXBElement;
}

public void setFooJAXBElement(JAXBElement<String> fooJAXBElement) {
    this.fooJAXBElement = fooJAXBElement;
}

Java Model

Root

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlType(propOrder={"fooString", "barString", "fooJAXBElement", "barJAXBElement"})
public class Root {

    private String fooString;
    private String barString;
    private JAXBElement<String> fooJAXBElement;
    private JAXBElement<String> barJAXBElement;

    private boolean setFoo;
    private boolean setBar;

    @XmlElement(nillable=true)
    public String getFooString() {
        return fooString;
    }

    public void setFooString(String foo) {
        this.fooString = foo;
        this.setFoo = true;
    }

    public boolean isSetFooString() {
        return setFoo;
    }

    @XmlElement(nillable=true)
    public String getBarString() {
        return barString;
    }

    public void setBarString(String bar) {
        this.barString = bar;
        this.setBar = true;
    }

    public boolean isSetBarString() {
        return setBar;
    }

    @XmlElementRef(name="fooJAXBElement")
    public JAXBElement<String> getFooJAXBElement() {
        return fooJAXBElement;
    }

    public void setFooJAXBElement(JAXBElement<String> fooJAXBElement) {
        this.fooJAXBElement = fooJAXBElement;
    }

    @XmlElementRef(name="barJAXBElement")
    public JAXBElement<String> getBarJAXBElement() {
        return barJAXBElement;
    }

    public void setBarJAXBElement(JAXBElement<String> barJAXBElement) {
        this.barJAXBElement = barJAXBElement;
    }

}

ObjectFactory

import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.*;
import javax.xml.namespace.QName;

@XmlRegistry
public class ObjectFactory {

    @XmlElementDecl(name="fooJAXBElement")
    public JAXBElement<String> createFooJAXBElement(String string) {
        return new JAXBElement<String>(new QName("fooJAXBElement"), String.class, string);
    }

    @XmlElementDecl(name="barJAXBElement")
    public JAXBElement<String> createBarJAXBElement(String string) {
        return new JAXBElement<String>(new QName("barJAXBElement"), String.class, string);
    }

}

Demo

Below is a full example to demonstrate the concepts discussed above.

input.xml

This document contains 2 elements explicitly marked with xsi:nil="true" and 2 other mapped elements that are absent.

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <barString xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
    <barJAXBElement xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
</root>

Demo

This demo code will read in the above XML and check whether the properties have been set as a result of the unmarshal.

import java.io.File;
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/forum20076018/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);

        System.out.println("Was fooString set?  " + root.isSetFooString());
        System.out.println("Was barString set?  " + root.isSetBarString());
        System.out.println("Was fooJAXBElement set?  " + (root.getFooJAXBElement() != null));
        System.out.println("Was barJAXBElement set?  " + (root.getBarJAXBElement() != null));

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }

}

Output

Below is the output from running the demo code. We see that all the is set checks are correct, but that the output only fully matches the input for the JAXBElement properties.

Was fooString set?  false
Was barString set?  true
Was fooJAXBElement set?  false
Was barJAXBElement set?  true
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <fooString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
    <barString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
    <barJAXBElement xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</root>