13

It seems the latest JAX-RS can handle methods returning java.util.List as the XMLRootElement but normal JAXB cannot. I would like to mimic what CXF and Jersey are doing.

In other words I would like to Marshal a List and just like CXF and Jersey do. Normally if you try to marshal a list with JAXB you get the Root Element exception. How do I get around this with out having to make a wrapping object?

EDIT: Thanks for the many answers but I'm very familiar with the @XmlElementWrapper but that does not even come close to simulating what JAX-RS is doing.

JAX-RS does this:

@XmlRootElement(name="dog")
public class Dog {
    private String name;
    public String getName() { return this.name; }
    //Setter also
}

Now if I serialize a list of dogs:

serialize(List<Dog> dogs);

XML should be (what JAX-RS does):

<dogs>
    <dog><name>Rascal</name></dog>
</dogs>

So you can see I don't want to have to make a wrapper object for every single domain object.

Adam Gent
  • 47,843
  • 23
  • 153
  • 203
  • 1
    I think this might be the answer https://jaxb.dev.java.net/guide/Different_ways_of_marshalling.html#Marshalling_a_non_element – Adam Gent May 13 '10 at 15:11
  • Looks like I could use the code in: org.apache.cxf.jaxrs.provider.AbstractJAXBProvider – Adam Gent May 13 '10 at 15:46
  • 2
    Unfortunately, the JAX-RS version is also not without its problems, see e.g. this one: http://stackoverflow.com/questions/6192389/root-element-name-in-collections-returned-by-resteasy – Arjan Tijms May 31 '11 at 22:11
  • I have seen issues trying to pass a list back through Jersey, so I am not convinced that Rest has solved this issue. – haskovec Apr 26 '12 at 16:47
  • I sort of gave up on the issue and just rely on Jackson (JSON) as the serialization technology which thankfully does a damn good job with handling lists. – Adam Gent Apr 26 '12 at 17:08

7 Answers7

8

Could you not simply add:

@XmlElementWrapper(name = "wrapperName")

No need to make a wrapper object. This will then be the route element in your marshalled XML response.

musefan
  • 47,875
  • 21
  • 135
  • 185
Ben
  • 93
  • 1
  • 4
  • Yes this is the correct way to ensure collections are wrapped. Annotate your list with the @XmlElementWrapper as per https://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlElementWrapper.html – Steve Swinsburg Nov 25 '14 at 00:34
3

I have a secret which allows me to avoid screwing with JAXB mappings entirely and have everything work magically.

Have used this approach for years, never spent even 5 minutes worrying about marshalling/unmarshalling. The secret is.... don't use JAXB. :)

In most of my projects which use JAX-RS, I configure Jersey to use xstream and let xstream figure out how to marshal/unmarshal for me. (or jackson for JSON)

Maybe there are some reasons to use JAXB instead of something like xstream/jackson, but I haven't found any yet.

ianpojman
  • 1,743
  • 1
  • 15
  • 20
3

I used a custom iterable list using the following code, hope this helps.

package bindings;

import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlType
@XmlRootElement
public class CustomBindList<V> implements Iterable<V>, Serializable {
        private static final long serialVersionUID = 4449835205581297830L;

        @XmlElementWrapper(name = "List")
        @XmlElement(name = "Entry")
    private final List<V> list = new LinkedList<V>();

    public CustomBindList() {
    }

    public void add(final V obj) {
            list.add(obj);
    }

    public V get(final int index) {
        return list.get(index);
    }

    @Override
    public Iterator<V> iterator() {
        return list.iterator();
    }

    public int size() {
        return list.size();
    }
}
1

mapping a List can be done like this..

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType
public class TestRootObject {

  @XmlElement(name = "testList") 
  private List<TestObj> testList;

  //getter setter
}
Sanath
  • 4,774
  • 10
  • 51
  • 81
1

check Jackson out, it is very compatible with JAXB bindings and using MappingJsonFactory you can actually interchangeably use Java to XML to Java to Json to Java.

skipy
  • 4,032
  • 2
  • 23
  • 19
1

I annotate my collections with @XmlJavaTypeAdapter(value = Adapter.class). The Adapter class extends from XmlAdapter<key, value> the key is a unique identifier to (un)marshal and the value is your domain object.

Maybe this can help you out. However you do have to create an adapter for every domain object.

siebz0r
  • 18,867
  • 14
  • 64
  • 107
1

Blaise Doughan I believe shows a good solution for this problem here: Is it possible to programmatically configure JAXB?

and

http://blog.bdoughan.com/2012/11/creating-generic-list-wrapper-in-jaxb.html

and somewhat here albeit slightly different use case:

http://blog.bdoughan.com/2012/02/xmlanyelement-and-xmladapter.html

Community
  • 1
  • 1
Adam Gent
  • 47,843
  • 23
  • 153
  • 203