Search code examples
javajaxbmoxy

JAXB/MOXy: Do not call XmlElementWrapper setter when element missing?


I have a list setter on a class that's annotated with both @XmlElementWrapper(name = "foos") and @XmlElement(name = "foo").

When I unmarshall XML that has no <foos></foos> or <foo/> elements, the setter is called and passed an empty list. Is there a way to get the following?:

  • When there is no <foos/>, do not call the setter. Or if the setter must be called, pass null.
  • When <foos/> is present but empty, pass an empty list to the setter.
  • When <foos> has one or more child <foo/> elements, pass a populated list.

Solution

  • You could use an XmlAdapter for this use case:

    input1.xml

    When there is no , do not call the setter. Or if the setter must be called, pass null.

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
        <child/>
    </root>
    

    input2.xml

    When is present but empty, pass an empty list to the setter.

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
        <child>
            <foos/>
        </child>
    </root>
    

    input3.xml

    When has one or more child elements, pass a populated list.

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
        <child>
            <foos>
                 <foo>Hello World</foo>
           </foos>
       </child>
    </root>
    

    Root

    import javax.xml.bind.annotation.XmlRootElement;
    import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    
    @XmlRootElement
    public class Root {
    
        private Child child;
    
        @XmlJavaTypeAdapter(ChildAdapter.class)
        public Child getChild() {
            return child;
        }
    
        public void setChild(Child child) {
            this.child = child;
        }
    
    }
    

    Child

    import java.util.List;
    
    public class Child {
    
        private List<String> strings;
    
        public List<String> getStrings() {
            return strings;
        }
    
        public void setStrings(List<String> strings) {
            System.out.println("setStrings");
            this.strings = strings;
        }
    
    }
    

    ChildAdapter

    import java.util.ArrayList;
    import java.util.List;
    
    import javax.xml.bind.annotation.adapters.XmlAdapter;
    
    public class ChildAdapter extends XmlAdapter<ChildAdapter.AdaptedChild, Child> {
    
        public static class AdaptedChild {
            public Foos foos;
        }
    
        public static class Foos {
            public List<String> foo;
        }
    
        @Override
        public Child unmarshal(AdaptedChild adaptedChild) throws Exception {
            Child child = new Child();
            Foos foos = adaptedChild.foos;
            if(null != foos) {
                List<String> foo = foos.foo;
                if(null == foo) {
                    child.setStrings(new ArrayList<String>());
                } else {
                    child.setStrings(foos.foo);
                }
            }
            return child;
        }
    
        @Override
        public AdaptedChild marshal(Child child) throws Exception {
            AdaptedChild adaptedChild = new AdaptedChild();
            List<String> strings = child.getStrings();
            if(null != strings) {
                Foos foos = new Foos();
                foos.foo = strings;
                adaptedChild.foos = foos;
            }
            return adaptedChild;
        }
    
    }
    

    Demo

    import java.io.File;
    
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.Marshaller;
    import javax.xml.bind.Unmarshaller;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            Object o;
    
            o = unmarshaller.unmarshal(new File("input1.xml"));
            marshaller.marshal(o, System.out);
    
            o = unmarshaller.unmarshal(new File("input2.xml"));
            marshaller.marshal(o, System.out);
    
            o = unmarshaller.unmarshal(new File("input3.xml"));
            marshaller.marshal(o, System.out);
        }
    
    }
    

    Output

    <?xml version="1.0" encoding="UTF-8"?>
    <root>
       <child/>
    </root>
    setStrings
    <?xml version="1.0" encoding="UTF-8"?>
    <root>
       <child>
          <foos/>
       </child>
    </root>
    setStrings
    <?xml version="1.0" encoding="UTF-8"?>
    <root>
       <child>
          <foos>
             <foo>Hello World</foo>
          </foos>
       </child>
    </root>