1

I'm starting with JAXB and I'm trying to read the following xml to map it to a class:

<element id="0">
        <type>1</type>
        <color>0</color>
        <size>1</size>
        <location>
          <absolute>
            <absolute-item>top</absolute-item>
            <absolute-item>left</absolute-item>
          </absolute>
          <relative>
            <right>0</right>
            <left>0</left>            
          </relative>
        </location>
    </element>

My problem comes when I try to map the nested elements like absolute, which can contain any number of <absolute-item> elements. I'm trying this right now:

public class Element {
    @XmlAttribute
    private int id;
    @XmlElement
    private int type;
    @XmlElement
    private int color;
    @XmlElement
    private int size;
    @XmlElementWrapper(name="absolute")
    @XmlElement(name="absolute-item")
    private ArrayList<String> absoluteItems;

    @Override
    public String toString() {
        return "Element "+id+" {" +
                "type=" + type +
                ", color=" + color +
                ", size=" + size +
                ", Location Relative: "+ absoluteItems
                +"}";
    }
}

I got the simple elements but not the nested one. Apparently I can't annotate to wrappers together, so I don't know how to fix it.

UPDATE:
I'm trying this as suggested. I'm getting an IllegalAnnotationExceptions because Element$Location.right is not a compilation property, but I don't know what it means. Should I create one more class for the <relative> element?

public class Element {
    @XmlAttribute
    private int id;
    @XmlElement
    private int type;
    @XmlElement
    private int color;
    @XmlElement
    private int size;

    @XmlElement(name="location")
    private Location location;

    public static class Location {
        @XmlElementWrapper(name="absolute")
        @XmlElement(name="absolute-item")
        private ArrayList<String> absoluteItems;


        @XmlElementWrapper(name="relative")
        @XmlElement(name="right")
        private int right;
        @XmlElement(name="left")
        private int left;

        @Override
        public String toString() {
            return "Location{" +
                    "Absolute=" + absoluteItems +
                    ", relative {right=" + right +
                    ", left=" + left +
                    "}}";
        }
    }
Jason Aller
  • 3,541
  • 28
  • 38
  • 38
Xhark
  • 781
  • 1
  • 9
  • 24
  • P.D: I'm aware there is another child in location(). Step by Step. – Xhark Mar 08 '18 at 19:36
  • Make a class for `location` containing `absolute/absoluteItems` mapping. – lexicore Mar 08 '18 at 21:53
  • And what about relative? Their elements are fixed. Do I have to Use an ElementWrapper to? – Xhark Mar 08 '18 at 22:01
  • You can but do not have to. – lexicore Mar 09 '18 at 06:37
  • Then what? Another nested class? Do i have to do one for each nesting? – Xhark Mar 09 '18 at 09:56
  • 1
    You should write several small Java classes (`Element`, `Location`, `Absolute`, `Relative`) corresponding to XML elements (``, ``, ``, ``) See also similar question [JAXB annotations for nested element lists](https://stackoverflow.com/questions/14202529/jaxb-annotations-for-nested-element-lists) – Thomas Fritsch Mar 09 '18 at 15:11
  • @Xhark Easy there. This depends on the logical structure first of all. In your case, yes, it seems like four classes (`Element`, `Location`, `Absolute` and `Relative`) are justified. Technically you can spare `Absolute`, but I won't do it. What's the problem with four classes? – lexicore Mar 09 '18 at 18:08
  • Your are right, I guess I was expecting something cleaner or more elegant(?). Thanks for the help. It works with the classes. – Xhark Mar 09 '18 at 18:19

1 Answers1

2

JAXB processing assumes a separate element definition for each complex element. A complex element means an element containing other elements.

The error you mention, referring to Element$Location, probably indicates the jaxb routines have found the annotation for the field "right" in the nested Location class; at runtime, nested classes have the name of the containing class, in this case Element, as a prefix separated from the nested class by a dollar sign.

To unmarshall (translate from xml text to Java code) the data structure you provide above, the JAXB routines would expect to find the following public class definitions:

Absolute Element Location ObjectFactory Relative

In the java code the jaxb compiler produces, the main element definition has its own class, and the following annotations:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
   "type",
    "color",
    "size",
    "location"
})
@XmlRootElement(name = "element")
public class Element 
{
   @XmlElement(required = true)
   protected BigInteger type;
      .
      .   additional element definitions go here
      . 
   // note: location here is a public class
   @XmlElement(required = true)
   protected Location location;
   // the attribute is defined thus:
   @XmlAttribute(name = "id")
   protected BigInteger id;

   /**
    * Gets the value of the type property.
    * @return BigInteger type code
    */
   public BigInteger getType() { return type; }

   /**
    * Sets the value of the type property.
    * @param value BigInteger type code to set
    */
   public void setType(BigInteger value) { this.type = value; }
      .
      . all other getters and setters defined here
      .
}

The element contained, in this case Location, has its own class with the following annotations:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Location", propOrder = {
   "absolute",
   "relative"
})
public class Location 
{
    protected Absolute absolute;
    protected Relative relative;
      .
      .  getters and setters go here: note these fields have no 
      .  annotations
}

Again, jaxb expects to find public Absolute and Relative classes. Since the absolute-item element can be repeated indefinitely, jaxb expects to find it defined as a collection (List), thus:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "Absolute", propOrder = {
   "absoluteItem"
})
public class Absolute 
{
   @XmlElement(name = "absolute-item", required = true)
   protected List<String> absoluteItem;

   /**
    * Gets the value of the absoluteItem property.
    * 
    * <p>This accessor returns a reference to the live list; any
    * modification to the returned list will be reflected in the 
    * JAXB object.
    * This is why there is not a setter for absoluteItem.
    * 
    * To add a new item, do as follows:
    *    getAbsoluteItem().add(newItem)
    */
   public List<String> getAbsoluteItem() 
   {
      if (absoluteItem == null) 
          absoluteItem = new ArrayList <> ();        
      return this.absoluteItem;
   }

}

In my experience, jaxb schema compiler, xjc, provides the easiest way, by far, to generate classes or use with jaxb. xjc takes an XML schema definition, and translates it into java code for you; you then have only to include the resulting java files in your project. Most Java development tools will handle this for you automatically. To generate the code I used as a source for the examples above (I did some condensing, I wrote the following schema:

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

<!--
  Component ==> example for stack overflow
  Purpose: show how the jaxb compiler works
 -->

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
           xmlns:desc = "http://sox.org/element"
           targetNamespace = "http://sox.org/element"
           jxb:version="2.0">

    <xs:element name="element">
       <xs:complexType>
          <xs:sequence>
             <xs:element name = "type" type = "xs:integer" />
             <xs:element name = "color" type = "xs:integer" />
             <xs:element name = "size" type = "xs:integer" />
             <xs:element name = "location" type = "desc:Location" />
          </xs:sequence>
          <xs:attribute name="id" type="xs:integer" use="optional"
                        default="0" />
       </xs:complexType>
    </xs:element>     

    <xs:complexType name="Location">
       <xs:sequence>
          <xs:element name = "absolute" type = "desc:Absolute" 
                      minOccurs="0" />
          <xs:element name = "relative" type = "desc:Relative" 
                      minOccurs="0" /> 
       </xs:sequence>
    </xs:complexType>

    <xs:complexType name="Absolute">
       <xs:sequence>
          <xs:element name="absolute-item" type="xs:string"  
                      maxOccurs="unbounded" />
       </xs:sequence>
    </xs:complexType> 

    <xs:complexType name="Relative">
       <xs:sequence>
          <xs:element name="right" type="xs:string" /> 
          <xs:element name="left" type="xs:string" /> 
       </xs:sequence>
    </xs:complexType>

John Spragge
  • 172
  • 2
  • 3