Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to unmarshall repeated nested classes with JAXB?

Tags:

java

jaxb

How can I instruct JAXB to process this ?

XML

<root>
 <parent>
    <child id="1" name="foo" />
 </parent>
 <parent>
    <child id="3" name="foo2" />
 </parent>
 <parent>
    <child id="4" name="bar2" />
 </parent>
 <parent>
    <child id="2" name="bar" />
 </parent>
</root>

Root.java

@XmlRootElement
public class Root {
   @XmlElement(name="parent/child")
   List<Child> allChildren;
}

This doesn't work ... allChildren is empty.

like image 714
Stephan Avatar asked Dec 20 '12 15:12

Stephan


People also ask

How JAXB unmarshalling works?

Unmarshal is the process of binding the XML document using JAXB compiler and generating mapping java classes then constructing the instances with values available in XML document. Marshal is the reverse process of it. First we construct the instances and then write a XML document using the constructed instances.

What is JAXB Unmarshalling?

Unmarshal a root element that is globally declared The JAXBContext instance maintains a mapping of globally declared XML element and type definition names to JAXB mapped classes. The unmarshal method checks if JAXBContext has a mapping from the root element's XML name and/or @xsi:type to a JAXB mapped class.


2 Answers

You could change your model and do the following:

Root

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
   @XmlElement(name="parent")
   List<Parent> allParents;
}

Parent

@XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
   @XmlElement(name="child")
   List<Child> allChildren;
}

UPDATE

Is it possible to avoid the parent class ?

There are a couple of different ways to accomplish this:

OPTION #1 - Any JAXB Implementation using XmlAdapter

You could use an XmlAdapter to virtually add in the Parent class.

ChildAdapter

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

public class ChildAdapter extends XmlAdapter<ChildAdapter.Parent, Child> {

    public static class Parent {
        public Child child;
    }

    @Override
    public Parent marshal(Child v) throws Exception {
        Parent parent = new Parent();
        parent.child = v;
        return parent;
    }

    @Override
    public Child unmarshal(Parent v) throws Exception {
        return v.child;
    }

}

Root

The @XmlJavaTypeAdapter annotation is used to reference the XmlAdapter.

import java.util.List;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

   @XmlElement(name="parent")
   @XmlJavaTypeAdapter(ChildAdapter.class)
   List<Child> allChildren;

}

Child

import javax.xml.bind.annotation.*;

@XmlAccessorType(XmlAccessType.FIELD)
public class Child {

    @XmlAttribute
    int id;

    @XmlAttribute
    String name;

}

OPTION #2 - Using EclipseLink JAXB (MOXy)

If you are using EclipseLink JAXB (MOXy) as your JAXB (JSR-222) implementation then you could do the following (Note: I'm the MOXy lead):

Root

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

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

   @XmlElement(name="parent")
   List<Child> allChildren;

}

Child

MOXy's @XmlPath annotation works pretty much the way you are trying to use the @XmlElement annotation in your post.

import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;

@XmlAccessorType(XmlAccessType.FIELD)
public class Child {

    @XmlPath("child/@id")
    int id;

    @XmlPath("child/@name")
    String name;

}

For More Information

  • http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
  • http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
like image 79
bdoughan Avatar answered Oct 13 '22 01:10

bdoughan


You will have to create a class representing the <parent> element, such as

@XmlAccessorType(XmlAccessType.FIELD)
public class Parent {
   @XmlElement(name="child")
   Child child;
}

You could then create a type adapter

public class ParentToChildAdapter extends XmlAdapter<Parent, Child> {
  public Parent marshal(Child c) {
    Parent p = new Parent();
    p.child = child;
    return p;
  }

  public Child unmarshal(Parent p) {
    return p.child;
  }
}

and use this on the root class

@XmlRootElement
public class Root {
   @XmlElement(name="parent")
   @XmlJavaTypeAdapter(ParentToChildAdapter.class)
   List<Child> allChildren;
}
like image 42
Ian Roberts Avatar answered Oct 13 '22 01:10

Ian Roberts