Over a million developers have joined DZone.

MOXy's @XmlVariableNode - Using a Map's Key as the Node Name

DZone's Guide to

MOXy's @XmlVariableNode - Using a Map's Key as the Node Name

· Java Zone
Free Resource

Build vs Buy a Data Quality Solution: Which is Best for You? Gain insights on a hybrid approach. Download white paper now!

People often ask me how they can map a java.util.Map such that the keys become the node names.  In this post I will demonstrate how this can be done using the new Variable Node mapping that we have added in EclipseLink MOXy.

You can try this out today using a nightly build of EclipseLink 2.6.0:


Below are the XML and JSON representations we will use in this example.  Each has node names that correspond to keys and contents that correspond to values of a Map.

XML (input.xml)

<?xml version="1.0" encoding="UTF-8"?>
JSON (Output)

   "A" : 1,
   "B" : 2

Java Model (Root)
We want a non-default XML (and JSON) representation for Map so we will use an XmlAdapter (see: JAXB and java.util.Map).  MOXy's @XmlPath extension will be used to prevent the contents of the adapted Map from being wrapped in a parent element (see: XPath Based Mapping).
package blog.variablenode.map;
import java.util.*;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
public class Root {
    private Map<String, Integer> map = new HashMap<String, Integer>();
XmlAdapter (MapAdapter) 
An XmlAdapter is used to convert an object for the purposes of marshalling/unmarshalling (see: XmlAdapter - JAXB's Secret Weapon).  In this example we convert the Map to an AdaptedMap that has a List of AdaptedEntry values.  These AdaptedEntry values have a field ( key) to represent the key.  It is this field that we will use with @XmlVariableNode (line 13).  We will mark the key field with @XmlTransient to prevent if from being marshalled/unmarshalled (line 20).

package blog.variablenode.map;
import java.util.*;
import java.util.Map.Entry;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlVariableNode;
public class MapAdapter extends XmlAdapter<MapAdapter.AdaptedMap, Map<String, Integer>> {
    public static class AdaptedMap {
        List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
    public static class AdaptedEntry {
        public String key;
        public Integer value;
    public AdaptedMap marshal(Map<String, Integer> map) throws Exception {
        AdaptedMap adaptedMap = new AdaptedMap();
        for(Entry<String, Integer> entry : map.entrySet()) {
            AdaptedEntry adaptedEntry = new AdaptedEntry();
            adaptedEntry.key = entry.getKey();
            adaptedEntry.value = entry.getValue();
        return adaptedMap;
    public Map<String, Integer> unmarshal(AdaptedMap adaptedMap) throws Exception {
        List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
        Map<String, Integer> map = new HashMap<String, Integer>(adaptedEntries.size());
        for(AdaptedEntry adaptedEntry : adaptedEntries) {
            map.put(adaptedEntry.key, adaptedEntry.value);
        return map;
Below is some demo code that can be run to prove that everything works.  The Root object will be instantiated from XML input and marshalled to create the JSON output.

package blog.variablenode.map;
import java.io.File;
import javax.xml.bind.*;
import org.eclipse.persistence.jaxb.MarshallerProperties;
public class Demo {
    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/blog/variablenode/map/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
        marshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
        marshaller.marshal(root, System.out);
Further Reading
If you enjoyed this post then you may also be interested in:

Build vs Buy a Data Quality Solution: Which is Best for You? Maintaining high quality data is essential for operational efficiency, meaningful analytics and good long-term customer relationships. But, when dealing with multiple sources of data, data quality becomes complex, so you need to know when you should build a custom data quality tools effort over canned solutions. Download our whitepaper for more insights into a hybrid approach.


Published at DZone with permission of Blaise Doughan, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}