Search code examples
javajaxbeclipselinkjaxb2moxy

Unexpected element (uri:"", local: "element"). Expected elements are <{}link> <{}size>


When trying to unmarshall this xml:

<holder>
  <name>a</name>
  <elements>
    <element>
      <name>elem</name>
    </element>
  </elements>
</holder>

I get the error unexpected element (uri:"", local:"element"). Expected elements are <{}link>,<{}totalSize> in the ValidationEventHandler and the tag <elements> (and therefore the elements field in Holder class) is ignored.

When generating the XML both link and totalSize are not outputted as they are nil.

JAVA MODEL

The hierarchy is a bit complex:

(Simplified for the sake of the question)

ElementRoot

abstract ElementRoot has the link member

public abstract class ElementRoot implements Serializable {

    protected String link;

    @XmlElement(name = "link")
    public String getLink() {
        return link;
    }

    public void setLink(String link) {
        this.link = link;
    }

}

Wrapper

abstract Wrapper extends ElementRoot and has the totalSize member

public abstract class Wrapper<T> extends ElementRoot {

    protected int totalSize;

    protected List<T> collection = new ArrayList<>();

    @XmlElement
    public int getTotalSize() {
        return totalSize;
    }

    public void setTotalSize(int totalSize) {
        this.totalSize = totalSize;
    }

    public abstract List<T> getCollection();
}

Holder

Holder extends ElementRoot

@XmlRootElement(name = "holder")
@XmlType(propOrder = {"name", "elements"})
public class Holder extends ElementRoot {

    private String name;

    private Elements elements;

    // setters and getters not annotated
}

Elements

Elements extends Wrapper and has a collection of Element

import java.util.Collection;
import javax.xml.bind.annotation.*;

@XmlRootElement(name = "elements)
public class Elements extends Wrapper {

    @Override
    @XmlElement(name="element")
    public Collection<Element> getElements() {
        return elements;
    }

    // No setter, to add getElements().add(element)
}

Element

Element extends ElementRoot

@XmlRootElement(name = "element")
@XmlType(propOrder = {"id", "name"})
public class Element extends ElementRoot {

     private Integer id;

     private String name;

     // setters and getters no annotated

}

ENVIRONMENT

I'm using java 7:

JAXB-api 2.2.7
MOXy 2.5.0

Solution

  • There appears to be a bug in EclipseLink JAXB (MOXy) for this use case related to the abstract getCollecion property. We have opened up the following bug that you can use to track our progress on this issue:

    WORK AROUND

    Wrapper

    We can use @XmlAccessorType(XmlAccessType.NONE) so that only annotated fields/properties will be processed (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).

    import java.util.*;
    import javax.xml.bind.annotation.*;
    
    @XmlAccessorType(XmlAccessType.NONE)
    public abstract class Wrapper<T> extends ElementRoot {
    
        protected int totalSize;
    
        protected List<T> collection = new ArrayList<>();
    
        @XmlElement
        public int getTotalSize() {
            return totalSize;
        }
    
        public void setTotalSize(int totalSize) {
            this.totalSize = totalSize;
        }
    
        public abstract List<T> getCollection();
    
    }
    

    Elements

    Since @XmlAccessorType is inherited by the subclasses we will specify XmlAccessType.PUBLIC to return things to normal. Note: I assume the getElements() method in your question should have been getCollection().

    import java.util.*;
    import javax.xml.bind.annotation.*;
    
    @XmlRootElement(name = "elements")
    @XmlAccessorType(XmlAccessType.PUBLIC_MEMBER)
    public class Elements extends Wrapper {
    
        @Override
        @XmlElement(name="element")
        public List<Element> getCollection() {
            return collection;
        }
    
        // No setter, to add getElements().add(element)
    }