简体   繁体   中英

Casting null java.lang.Integer from JAXB produces 0, not null

I have the following XML:

<person>
    <id/>
    <Identifier/>
    <Surname>TEST</Surname>
    <Forename1>TEST</Forename1>
    <Forename2/>
    <Title>MR</Title>
    <Gender>M</Gender>
</person>

Produced by serialising the following class:

package anonymised.packagename;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
@XmlAccessorType(XmlAccessType.FIELD)

@XmlType(name = "", propOrder = {
    "Id",
    "Identifier",
    "Surname",
    "Forename1",
    "Forename2",
    "Title",
    "Sex"
})

@XmlRootElement(name = "person")
public class person{

    @XmlElement(name = "id", nillable=true)
    protected Integer Id;
    public Integer getId(){
        return Id;
    }

    public void setId(Integer value){
        this.Id = value;
    }

    @XmlElement(name = "Identifier", nillable=true)
    protected String Identifier;
    public String getIdentifier(){
        return Identifier;
    }

    public void setIdentifier(String value){
        this.Identifier = value;
    }

    @XmlElement(name = "Surname", nillable=true)
    protected String Surname;
    public String getSurname(){
        return Surname;
    }

    public void setSurname(String value){
        this.Surname = value;
    }

    @XmlElement(name = "Forename1", nillable=true)
    protected String Forename1;
    public String getForename1(){
        return Forename1;
    }

    public void setForename1(String value){
        this.Forename1 = value;
    }

    @XmlElement(name = "Forename2", nillable=true)
    protected String Forename2;
    public String getForename2(){
        return Forename2;
    }

    public void setForename2(String value){
        this.Forename2 = value;
    }

    @XmlElement(name = "Title", nillable=true)
    protected String Title;
    public String getTitle(){
        return Title;
    }

    public void setTitle(String value){
        this.Title = value;
    }

    @XmlElement(name = "Gender", nillable=true)
    protected String Gender;
    public String getGender(){
        return Gender;
    }

    public void setGender(String value){
        this.Gender = value;
    }
}

I used the "set" methods on the object and JAXB serialisation to produce the XML above, choosing java.lang.Integer objects precisely because they are nullable.

I then used the following code to deserialise the XML (represented by xmlString) back to the object:

DocumentBuilder builder = fac.newDocumentBuilder();
Document doc = builder.parse(new InputSource(new StringReader(xmlString)));
String className = "anonymised.packagename." + doc.getDocumentElement().getNodeName();
Class<?> c = Class.forName(className);
JAXBContext context = JAXBContext.newInstance(c);
Unmarshaller um = context.createUnmarshaller();
Object o = c.cast(um.unmarshal(new StringReader(xmlString)));

JAXBContext jbc = JAXBContext.newInstance(c);
Marshaller jm = jbc.createMarshaller();

jm.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

jm.marshal(o, new File("C:\\person.xml"));

Which produced the following XML file:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
    <id>0</id>
    <Identifier/>
    <Surname>TEST</Surname>
    <Forename1>TEST</Forename1>
    <Forename2></Forename2>
    <Title>MR</Title>
    <Gender>M</Gender>
</person>

The java.lang.Integer that was null is now 0. How can this be? Judging by breakpoints, the change from null to 0 occurs during the casting process, but I can't figure out why exactly this is. Is there some kind of conversion to a simple int during the casting process? Is there a way to avoid this phenomenon? I thought adding "nillable" to the XML Element declaration would help, but it hasn't.

JAXB doesn't consider any empty element to be a valid representation of null . If you introspect the indentifier property of type String you'll see it is "" after you unmarshal the empty element.

The two valid representations of null are:

  1. Missing element, corresponds to minOccurs="0" in the XML Schema. This is the default behaviour.
  2. xsi:nil="true" on the element. This corresponds to nillable="true" in the XML Schema. If you annotate your property with @XmlElement(nillable=true) you will get this behaviour.

UPDATE

Thanks for your answer. On point 2, I have annotated my property with @XmlElement(nillable = true) in the class above, but I haven't got the behaviour you describe in my XML. Does your answer mean that, in the production of any XML that should be null, the attribute xsi:nil needs to explicitly be set to true?

When you have @XmlElement(nillable=true) the expectation is that the corresponding XML element has the xsi:nil attribute set to true like the following:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<person>
    <id xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</person>

I'm happy with the idea that the Identifier element is an empty String, not null, but the id element, because it's an Integer should be null, not 0, right?

You can definitely make that argument. With the JAXB RI if you change your Integer property to Long you will see the behaviour you are looking for.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM