JAXB cannot write a bare List or Set to XML, so we put the list inside a class marked with @XmlRootElement. On the list field, we add the @XmlElementWrapper and @XmlElement annotations, which name the grouping element and the item elements.
We need a list in XML whenever an app exports or imports a collection, such as the ingredients of a recipe that a cooking site sends to a partner system as an XML feed.
The following example marshals a Recipe with two ingredients and reads the XML back. Marshalling turns the Java object into XML, and unmarshalling reads the XML back into an object with the same list.
@XmlRootElement(name = "recipe")
@XmlAccessorType(XmlAccessType.FIELD)
public class Recipe {
@XmlAttribute
private String name;
@XmlElementWrapper(name = "ingredients") // <ingredients> around the items
@XmlElement(name = "ingredient") // one <ingredient> per list item
private List<Ingredient> ingredients = new ArrayList<>();
// constructors, getters, toString()
}
Recipe recipe = new Recipe("Pancakes");
recipe.getIngredients().add(new Ingredient("flour", 200));
recipe.getIngredients().add(new Ingredient("milk", 300));
JAXBContext context = JAXBContext.newInstance(Recipe.class);
// 1. Java object to XML
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter out = new StringWriter();
marshaller.marshal(recipe, out);
String xml = out.toString(); // the XML below
// 2. XML back to the Java object
Recipe copy = (Recipe) context.createUnmarshaller().unmarshal(new StringReader(xml));
List<Ingredient> list = copy.getIngredients(); // list = [flour 200g, milk 300g]
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<recipe name="Pancakes">
<ingredients>
<ingredient name="flour" grams="200"/>
<ingredient name="milk" grams="300"/>
</ingredients>
</recipe>
Notice that the <ingredients> element comes from @XmlElementWrapper, and each <ingredient> element comes from one list item.
Next, we look at why a bare List fails and what the real error message says. After that, we compare a root class with a generic wrapper class, and finish with Set and Map fields and with empty or null lists.
1. Why JAXB Cannot Marshal a Bare List
JAXB (Jakarta XML Binding) is the Java API that turns objects into XML and back, and we tell it what to write with annotations on our classes. Every XML document has one top-level element (the root element), and JAXB takes its name from the @XmlRootElement annotation of the object we pass to marshal().
ArrayList, HashSet and the other Java collections have no JAXB annotations, so JAXB has no name for the root element and throws an exception. The three common attempts fail with different errors.
| Attempt | Result |
|---|---|
| JAXBContext.newInstance(List.class) | IllegalAnnotationsException: java.util.List is an interface, and JAXB can’t handle interfaces. |
| JAXBContext.newInstance(ArrayList.class), then marshal(arrayList, out) | MarshalException with the linked exception unable to marshal type “java.util.ArrayList” as an element because it is missing an @XmlRootElement annotation |
| JAXBContext.newInstance(Ingredient.class), then marshal(arrayList, out) | JAXBException: class java.util.ArrayList nor any of its super class is known to this context. |
The second case is the most confusing one because its getMessage() returns null, so the log shows no reason. The real text is in a second exception that getLinkedException() returns.
jakarta.xml.bind.MarshalException: null
com.sun.istack.SAXException2: unable to marshal type "java.util.ArrayList" as an element because it is missing an @XmlRootElement annotation
The fix is always the same, because we give the collection a root element. There are two ways to do that.
- Write a root class such as Recipe with a List field (section 2), which can hold other fields too, like the name attribute.
- Write one generic Wrapper<T> class and marshal it inside a JAXBElement, which sets the root element name at runtime (section 3).
1.1. Maven Dependencies for Jakarta XML Binding 4
Java 11 removed JAXB from the JDK, and the package name later changed from javax.xml.bind to jakarta.xml.bind. So we add two dependencies from Maven Central, namely the API and the reference implementation that does the real work.
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>4.0.5</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>4.0.9</version>
<scope>runtime</scope>
</dependency>
Our code imports only jakarta.xml.bind types, so jaxb-runtime is needed only when the program runs, and the runtime scope is enough. The older com.sun.xml.bind:jaxb-impl artifact also works, because it is the same reference implementation in a different packaging.
2. Marshal and Unmarshal a List Example
The following example is a recipe with a list of ingredients. All classes are in the Core-Java repository on GitHub, together with a CollectionsDemo class that prints every result in the snippets. The code uses Java 21 bytecode, jakarta.xml.bind-api 4.0.5 and jaxb-runtime 4.0.9, and runs on JDK 25.
2.1. The Item Class and the Root Class
Ingredient is the type of each item in the list, and two annotations control how JAXB writes it.
- @XmlAccessorType(XmlAccessType.FIELD) tells JAXB to read and write the fields, without getters.
- @XmlAttribute writes a field as an XML attribute, like name=”flour”, instead of a child element.
Fields and classes accept other JAXB annotations too, such as @XmlTransient.
@XmlRootElement(name = "ingredient") // needed only for the generic wrapper in section 3
@XmlAccessorType(XmlAccessType.FIELD)
public class Ingredient {
@XmlAttribute
private String name;
@XmlAttribute
private int grams;
public Ingredient() { } // JAXB calls the no-arg constructor
public Ingredient(String name, int grams) { ... }
}
The root class is Recipe from the intro. Recipe and Ingredient both need a no-arg constructor, because JAXB calls that constructor to create each object during unmarshalling. Without it, JAXB fails with the JAXB no default constructor found error.
Two annotations on the ingredients field decide how the list looks in the XML.
- @XmlElement(name = “ingredient”) sets the element name of each item. Without the annotation, JAXB uses the field name, so every item would be written as <ingredients>.
- @XmlElementWrapper(name = “ingredients”) adds one extra element around all items, and section 2.4 shows the XML with and without it.
2.2. Marshal the List to XML
We create the JAXBContext once, for the root class only, because JAXB sees the List<Ingredient> field type and finds the Ingredient class on its own. The JAXB_FORMATTED_OUTPUT property adds line breaks and indentation to the XML.
JAXBContext context = JAXBContext.newInstance(Recipe.class);
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(recipe, System.out); // XML on the console
marshaller.marshal(recipe, new File("recipe.xml")); // XML in a file
Both calls write the XML document from the intro. The JAXB Marshaller can also write to a Writer, an OutputStream or a DOM node.
A JAXBContext is expensive to create and thread-safe, so we keep one JAXBContext per set of classes and share it. A Marshaller is cheap but not thread-safe, so we create a new one for each call. For example, a web service that returns recipes as XML creates the JAXBContext once at startup, and each request creates its own Marshaller.
2.3. Unmarshal the XML Back to a List
The Unmarshaller reads the XML and creates a Recipe, and for each <ingredient> element, it adds one Ingredient to the list.
Unmarshaller unmarshaller = context.createUnmarshaller();
Recipe copy = (Recipe) unmarshaller.unmarshal(new File("recipe.xml"));
List<Ingredient> items = copy.getIngredients(); // [flour 200g, milk 300g]
String listType = copy.getIngredients().getClass().getName(); // java.util.ArrayList
The same unmarshal() call also reads from a StringReader, an InputStream or a URL, which are the other inputs of the JAXB Unmarshaller. When the List field has no initial value, JAXB creates an ArrayList for the field.
2.4. Output With and Without @XmlElementWrapper
The repository has a FlatRecipe class, which is a copy of Recipe without @XmlElementWrapper. Both classes hold the same data, but the XML looks different.

| Mapping on the List field | XML of the list |
|---|---|
| @XmlElementWrapper(name = “ingredients”) + @XmlElement(name = “ingredient”) | <ingredients> with the <ingredient> items inside |
| @XmlElement(name = “ingredient”) only | <ingredient> items under <recipe>, without a grouping element |
| No annotation | <ingredients> items under <recipe>, one per list item (the field name) |
When we unmarshal, the mapping must match the XML. A mismatch does not throw an exception, because JAXB skips the elements it does not know, so the list stays empty.
// wrapped XML (<ingredients><ingredient .../></ingredients>) read by FlatRecipe
FlatRecipe flat = (FlatRecipe) flatUnmarshaller.unmarshal(new StringReader(wrappedXml));
// FlatRecipe[name=Pancakes, ingredients=[]]
// flat XML (<ingredient .../> under <recipe>) read by Recipe
Recipe recipe = (Recipe) unmarshaller.unmarshal(new StringReader(flatXml));
// Recipe[name=Pancakes, ingredients=[]]
We use the wrapper when the XML has a grouping element, or when the root class has several lists and we want to keep them apart. Without a wrapper, the XML is shorter, and the flat shape fits formats that repeat the items under the root element.
3. Generic Wrapper With JAXBElement
A root class per list type is clear, but a project with many lists ends up with many small classes, such as Ingredients, Users and Orders. A generic Wrapper<T> class handles every list with one class.
The items field uses @XmlAnyElement(lax = true). @XmlAnyElement accepts any element in the XML, and lax = true adds one more step. When an element name matches a class that has @XmlRootElement, JAXB creates an object of that class.
public class Wrapper<T> {
@XmlAnyElement(lax = true)
private List<T> items = new ArrayList<>();
public Wrapper() { }
public Wrapper(List<T> items) { this.items = items; }
public List<T> getItems() { return items; }
}
Wrapper has no @XmlRootElement, so JAXB does not know its root name, and we set the root name with a JAXBElement. A JAXBElement pairs a value with an element name given as a QName, for example new QName(“ingredients”), so we pick the root name in code at runtime.
JAXBContext context = JAXBContext.newInstance(Wrapper.class, Ingredient.class);
List<Ingredient> list = List.of(new Ingredient("flour", 200), new Ingredient("milk", 300));
JAXBElement<Wrapper> root = new JAXBElement<>(
new QName("ingredients"), Wrapper.class, new Wrapper<>(list));
marshaller.marshal(root, System.out);
<ingredients>
<ingredient name="flour" grams="200"/>
<ingredient name="milk" grams="300"/>
</ingredients>
To read the XML back, we call the two-argument unmarshal(Source, Class), which reads the root element into Wrapper whatever the root name is. It returns a JAXBElement again, and getValue() gives us the Wrapper.
Wrapper<?> wrapper = unmarshaller
.unmarshal(new StreamSource(new StringReader(xml)), Wrapper.class)
.getValue();
List<?> items = wrapper.getItems(); // [flour 200g, milk 300g]
Class<?> itemType = wrapper.getItems().get(0).getClass(); // Ingredient
Every item class must have @XmlRootElement and must be passed to JAXBContext.newInstance(), otherwise JAXB does not create our object. Say the XML also holds <spice>salt</spice>, and no class maps spice. JAXB does not throw an error but keeps that element as a raw DOM Element, so the list holds a com.sun.org.apache.xerces.internal.dom.ElementNSImpl next to the Ingredient objects. Code that casts every item to Ingredient then throws ClassCastException at that item.
Each approach has its own strengths, and the generic wrapper trades type safety for fewer classes.
| Dedicated root class (Recipe) | Generic Wrapper<T> + JAXBElement | |
|---|---|---|
| Classes to write | One per list type | One for all lists |
| Root element name | Fixed in @XmlRootElement | Chosen at runtime with a QName |
| Extra fields and attributes | Yes, like name | No |
| Item class needs @XmlRootElement | No | Yes |
| Type safety on unmarshal | List<Ingredient> | List<?>, unknown elements become DOM nodes |
| XSD generation | Clear schema | xs:any in the schema |
A JAXBElement is also the way to marshal without @XmlRootElement and unmarshal without @XmlRootElement for a single object.
4. Marshalling a Set and Its Order
A Set field uses the same annotations as a List. Our TaggedRecipe class has a Set<String> of tags, and for strings, JAXB writes each value as the text of one element, like <tag>quick</tag>.
@XmlElementWrapper(name = "tags")
@XmlElement(name = "tag")
private Set<String> tags = new LinkedHashSet<>();
JAXB writes the items in the order the collection returns them in a loop. A List keeps the order in which we added the items, whereas the order of a Set depends on the Set class. We added the tags quick, breakfast, sweet and vegetarian, in that order, to three sets.
| Set implementation | Order of the <tag> elements |
|---|---|
| HashSet | quick, vegetarian, breakfast, sweet (hash order, can change between JDK versions) |
| LinkedHashSet | quick, breakfast, sweet, vegetarian (insertion order) |
| TreeSet | breakfast, quick, sweet, vegetarian (sorted) |
Unmarshalling a Set has two more effects. A Set drops duplicate elements without an error, and the initial value of the field decides which Set class JAXB fills. We read this XML into two classes.
<recipe><tags><tag>sweet</tag><tag>quick</tag><tag>sweet</tag><tag>breakfast</tag></tags></recipe>
private Set<String> tags = new LinkedHashSet<>(); // [sweet, quick, breakfast] LinkedHashSet, XML order kept
private Set<String> tags; // [quick, sweet, breakfast] JAXB creates a HashSet
In the first class, JAXB adds the tags to our LinkedHashSet, so the XML order stays. In the second class, the field is null, so JAXB creates a HashSet and the order is lost. To keep the XML order of a Set, we start the field with a LinkedHashSet. A TreeSet works too when we want sorted output, and when the order or the duplicates matter, a List is the better field type.
5. Marshalling a Map With an XmlAdapter
A Map is not a Collection, so a Map field does not produce the item elements of sections 2 to 4. The reference implementation still marshals a Map<String, Integer> field without extra code, but the output uses generic entry, key and value elements.
<pantry>
<stock>
<entry>
<key>apple</key>
<value>5</value>
</entry>
<entry>
<key>banana</key>
<value>3</value>
</entry>
</stock>
</pantry>
For our own XML shape, we write an XmlAdapter (a converter class with two methods). Its marshal() method turns the Map into a list-based class that JAXB can write, and unmarshal() turns that class back into a Map. The adapter in the repository turns each map entry into an Item with a name attribute and the count as its text.
public class StockAdapter extends XmlAdapter<StockAdapter.Items, Map<String, Integer>> {
public static class Items {
@XmlElement(name = "item")
public List<Item> list = new ArrayList<>();
}
public static class Item {
@XmlAttribute public String name;
@XmlValue public int count;
}
@Override
public Items marshal(Map<String, Integer> map) { ... } // Map -> Items
@Override
public Map<String, Integer> unmarshal(Items items) { ... } // Items -> Map
}
@XmlJavaTypeAdapter(StockAdapter.class)
private Map<String, Integer> stock = new TreeMap<>();
<pantry>
<stock>
<item name="apple">5</item>
<item name="banana">3</item>
</stock>
</pantry>
Unmarshalling this XML gives {apple=5, banana=3} back. The same adapter idea works for a JAXB HashMap with object values, and for fields that hold an interface type through an XmlAdapter with interfaces.
6. Empty and Null Collections
To most of our code, an empty list and a null list both mean “no ingredients”, but JAXB writes them differently. With @XmlElementWrapper, an empty list writes an empty wrapper element, and a null list writes nothing. Without a wrapper, both cases write nothing.
We can also mark a null list in the XML. The nillable = true attribute of @XmlElementWrapper writes the wrapper with xsi:nil=”true”, which means “this value is null”.
| Mapping | Empty list | Null list |
|---|---|---|
| @XmlElementWrapper | <ingredients/> | element left out |
| @XmlElement only (no wrapper) | element left out | element left out |
| @XmlElementWrapper(nillable = true) | <ingredients/> | <ingredients xsi:nil=”true” xmlns:xsi=”…”/> |
On the way back, the result depends on the XML and on the initial value of the field. Recipe starts the list with new ArrayList<>(), whereas NillableRecipe leaves the field null.
| XML | Recipe (field initialized) | NillableRecipe (field null) |
|---|---|---|
| No <ingredients> element | [] | null |
| <ingredients/> | [] | [] |
| <ingredients xsi:nil=”true”/> | [] | null |
So when the XML has no list, Recipe still gives an empty list, and NillableRecipe gives null. Starting the field with an empty ArrayList or LinkedHashSet removes most null checks. We use nillable = true only when the XML reader must tell “no list” apart from “empty list”.
7. JAXB List FAQs
7.1. Why Is My List Empty After Unmarshalling?
Because JAXB ignores elements that the class does not map, a wrong element name gives an empty list without an exception. Three causes are common.
- The XML has a grouping element but the field has no @XmlElementWrapper, or the field has the wrapper but the XML does not (section 2.4).
- The @XmlElement name differs from the item element name. For example, the XML has ingredient, but the field uses the default name ingredients.
- The XML uses a namespace that the classes do not declare.
If we want an error instead of an empty list, we set a ValidationEventHandler that returns false on the Unmarshaller from section 2.3.
7.2. Do I Need Setters for the List Field?
No. With @XmlAccessorType(XmlAccessType.FIELD), JAXB reads and writes the field itself, so our Recipe class works without a setter. The setIngredients() method in the repository exists only for the null-list demo in section 6.
7.3. Does Old javax.xml.bind Code for Lists Still Work?
Yes, after a small change. The annotations and the wrapper approach are the same in Jakarta XML Binding 4, so we replace javax.xml.bind with jakarta.xml.bind in all imports and use the two dependencies from section 1.1. A class must not mix javax.xml.bind and jakarta.xml.bind annotations, because the Jakarta runtime does not read the old javax ones.
8. Conclusion
JAXB needs a root element, so a List or Set always goes inside a root class with @XmlRootElement or a generic Wrapper inside a JAXBElement. @XmlElement names the items, and @XmlElementWrapper adds the grouping element. The mapping must match the XML, or the list stays empty without an error.
A Set is written in its loop order, so a LinkedHashSet field keeps the XML order. A Map gets its own XML shape through an XmlAdapter, and a collection field that starts as an empty collection avoids null after unmarshalling.
9. References
- XmlElementWrapper JavaDoc (Jakarta XML Binding 4.0)
- XmlAnyElement JavaDoc
- JAXBElement JavaDoc
- XmlAdapter JavaDoc
- Jakarta XML Binding 4.0 specification
- JAXB reference implementation
Happy Learning !!
I have this exact problem but with the addition of this data being inside another root object which I can’t get to work.
Put your employees inside tags. How to you make a class called Company that will unMarshall the employees? I have success with the pattern that you gave, but this one last step returns null no matter how I try?
Hello,
I am Creating three pojo classess(Details,Subscriber,Publisher).In Details class i am 2 creating list of subscriber type and publisher type.in Main Method i am creating their instance and adding in a list.But i am getting all the elements from each list together.But what i want is first element from subscriber and then publisher and then again 2nd element from subscriber and then publisher.Below are my Actual output and Expected Output
Interesting issue. I will try this in free time.
This example is what I needed to build a xml file from an object. I was fighting with the ArrayList of objects setter for two evenings.
Thanks a lot
Hi Lokesh,
Nice example. But i didn’t got one thing. If we want to display the id of the first employee only and nothing else Then how will you do it. Because here all the contents stored in the list is being displayed. If I want a single data stored in the List then how will i get it.
Then probably, you should be using xpath. JAXB will not be a good solution in this particular case.
Hi Lokesh, Nice content. i need clarity. To convert java object to xml representation, we need have to JAXB.
Without JAXB is it possible? I am using spring restservices with JAXB.
No, JAXB is not required. You can use MOXy as well. Idea is that you need to use some interface which is capable of marshaling and unmarshalling.
OR, you can directly build your XML object and send in response.
I have one such example for JSON here: https://howtodoinjava.com/jersey/jax-rs-jersey-moxy-json-example/
You can use it for building XML by using https://jersey.github.io/
So to convert java object to xml or json format we need other interfaces like JAXB or Jackson.
Other wise we can directly send our java object in the form of xml or json form as our wish.
Mostly we use JAXB and Jackson , am I correct Lokesh?
I have some confusion in web services while doing conversions like this. That’s why I am asking.
You are right.
When unmarshalling, is there a way to convert List types i.e. where
, into Arrays i.e.
instead of
.
I think it can be achieved by using a bindings file but I am not aware of the exact configuration to put in this file. Can you help?
Hi Hemlata,
Did you get any solution for this Problem? Please revert if you had this sorted.
Hi, how can I run this without maven? I downloaded the three source files and try to compile and run with javac and java, it compiled ok, but got “could not find or load main class TestEmployeeMarshing” error when running. I used the same thing in STS and it worked fine? Can you help me? Thank you.
Add class files path in classpath parameter.
thank u for the article, didnt u forget to annotate the employee attributes with @XmlElement
id, firstName etc ?
When you use
@XmlAccessorType (XmlAccessType.FIELD)at class, then@XmlElementbecomes optional.I want to have the Employees class to be a property of the Department class, so that the root node is the Department and the XML looks like:
How should I annotate the Department, The Employees, and the Employee classes to generate such an XML?
Many thanks! It really helps my work.
Hi ,
I want to add a tostring method in the autogenerated class how can i do this
How you are generating the class? This
SA threadhas good information. Let me know if something else you want to know?Thanks and it is a good basic tutorial !! :)
Can any one help me in detail how to extract the records from the belwo XML snippet
12
PRASAD56
Chennai
2014-10-20T00:00:00+05:30
pkk@gmail.co.in
2014-08-01T00:00:00+05:30
BSM_RESTeter
9916347942
9916347942
944990031
22
3
prasadk
10007896
18
PRASAD61
Chennai
2014-10-24T00:00:00+05:30
pkk@gmail.co.in
2014-08-01T00:00:00+05:30
BSM_RESTeter
9916347942
9916347942
9916347942
23
prasadk
10007896
1
1
2
12
Please read the message above in red to correctly post the code.
Hi lokesh ,I do not see any message in red
In comment box, please put your code inside [java] … [/java] OR [xml] … [/xml] tags otherwise it may not appear as intended. Tags are in small letters.
Hi Lokesh ,Below is my XML snippet.Please suggest a best way to extract the two records
Use jaxb with namespaces. An example could be: Namespace_Prefix_Mapper
You need to google more for this.
But in the unmarshalling example, you never defined getID() and getFirstName() so how would this work?
You see that I have written “//Getters and Setters”. Basically, I didn’t included them in example code here just to reduce the length of page.
how to migrate my jaxb 1.0 classes to 2.0
There’s an existing program, where marshalling is included already. The problem is to unmarshal it. Whatever I try, it is not possible to unmarshal it. Can i send you a mail or something describing the problem?
Can you please elaborate more your problem. Please provide some error logs OR code in which you are facing the error.
Suppose i have a list of employees withouth the wrapper tags i.e
…
…
How do i unmarshall it to a list/set ?
Please paste the code inside
[ java] ... [/java]OR[ xml] ... [/xml]tags.Note i don’t have the outer employees tag
You must have any root element in XML. In above xml snippet, there is no root element.
thanks, very helpful example.
Hi. I have got some java files from an xsd file using jaxb xcj command line..now i have to convert them to an xml file..i came to know that using schemagen of jaxb it can be done..i tried but it is giving me an xsd file..what i want is xml equivalent of all those java files plz reply asap..
you need to use jaxb.
lokesh but im getting xsd file using that as i mentioned.what i want is an xml file
You can refer, what I mean, here in few examples: https://howtodoinjava.com/jaxb/jaxb-annotations/
OR//
Give me a sample class and sample XML what you want in output.
Nice simple example. Helped me…
how to Junit this unmarshal object?