Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB - empty tags with no xsi:nil

I have a String property in an object annotated as follows:

@XmlElement(name = "Item", required = true, nillable = true)
private String item;

The result after marshaling is

<Item xsi:nil="true"/>

while I would like it to be

<Item/>

since the third-party service accepting my XML messages wants it like the latter case. I am using jaxb2. Does anyone knows how I could possibly do this?

Thanks a lot

like image 357
gpol Avatar asked Jan 24 '12 12:01

gpol


1 Answers

Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.

The following example requires the use of MOXy as the JAXB provider. This is because the JAXB RI does not call the XmlAdapter when the field/property is null. For information on specifying MOXy as your JAXB provider see:

  • http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html

StringAdapter

The XmlAdapter will convert the String value to an object with a property annotated with @XmlValue.

package forum8986842;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class StringAdapter extends XmlAdapter<StringAdapter.AdaptedString, String>{

    @Override
    public String unmarshal(AdaptedString adaptedString) throws Exception {
        if(null == adaptedString) {
            return null;
        }
        String string = adaptedString.value;
        if("".equals(string)) {
            return null;
        }
        return string;
    }

    @Override
    public AdaptedString marshal(String string) throws Exception {
        AdaptedString adaptedString = new AdaptedString();
        adaptedString.value = string;
        return adaptedString;
    }

    public static class AdaptedString {
        @XmlValue public String value;
    }

}

Root

The @XmlJavaTypeAdapter annotation is used to specify the XmlAdapter:

package forum8986842;

import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

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

    private String item;

    @XmlElement(name = "Item", required = true, nillable = true)
    @XmlJavaTypeAdapter(StringAdapter.class)
    public String getItem() {
        return item;
    }

    public void setItem(String item) {
        this.item = item;
    }

}

Demo

The following code can be used to demonstrate the above mapping. Two documents are used one with an empty Item element, and the other with a populated Item element.

package forum8986842;

import java.io.StringReader;
import javax.xml.bind.*;

public class Demo {

    private JAXBContext jc;

    public Demo() throws JAXBException {
        jc = JAXBContext.newInstance(Root.class);
    }

    public static void main(String[] args) throws Exception {
        Demo demo = new Demo();
        demo.demo("<Root><Item/></Root>");
        demo.demo("<Root><Item>Hello World</Item></Root>");
    }

    private void demo(String xml) throws JAXBException {
        System.out.println("\n\nINPUT:  " + xml);
        StringReader stringReader = new StringReader(xml);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Root root = (Root) unmarshaller.unmarshal(stringReader);

        System.out.println("ITEM:   " + root.getItem());

        System.out.print("OUTPUT: ");
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
        marshaller.marshal(root, System.out);
    }

}

Output

The following is the output from running the demo code:

INPUT:  <Root><Item/></Root>
ITEM:   null
OUTPUT: <Root><Item/></Root>

INPUT:  <Root><Item>Hello World</Item></Root>
ITEM:   Hello World
OUTPUT: <Root><Item>Hello World</Item></Root>

For More Information

  • http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
  • http://blog.bdoughan.com/2011/06/jaxb-and-complex-types-with-simple.html
  • http://blog.bdoughan.com/search/label/XmlAdapter
like image 146
bdoughan Avatar answered Oct 04 '22 04:10

bdoughan