users@jaxb.java.net

Re: Custom mapping for Maps in JAXB

From: Leo Romanoff <romixlev_at_yahoo.com>
Date: Sun, 20 Jun 2010 23:24:51 -0700 (PDT)

Hi,

Here is the most convenient and pretty simple solution for the problem I've
implemented so far based on Wolfgang's proposals:

In your JAXB-annotated class you put the following annotations for your map
field:

@XmlRootElement(name="YourClassElementName")
class YourClass {
...

@XmlElement(name="YourMapElementName")
// Here is the trick: Use use special map adapter for maps (see below)
@XmlJavaTypeAdapter(JsonMapAdapter.class)
private Map<String,String> yourMap = new LinkedHashMap<String,String>();

...
}

and the JsonMapAdapter class can be defined somewhere like this:

package your.jaxb.adapters;

import java.util.LinkedHashMap;
import java.util.Map;

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

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

public class JsonMapAdapter extends XmlAdapter<Object, Map<?, ?>> {

        @Override
        public Map<?, ?> unmarshal(Object domTree) {
                Map<Object, Object> map = new LinkedHashMap<Object, Object>();
                Element content = (Element) domTree;
                NodeList childNodes = content.getChildNodes();
                if (childNodes.getLength() > 0) {
                        for (int i = 0; i < childNodes.getLength(); i++) {
                                Node child = childNodes.item(i);
                                String key = child.getNodeName();
                                // Skip text nodes
                                if (key.startsWith("#"))
                                        continue;
                                String value = ((Text) child.getChildNodes().item(0))
                                                .getWholeText();
                                map.put(key, value);

                        }
                }
                return map;
        }

        @Override
        public Object marshal(Map<?, ?> map) {
                try {
                        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
                        DocumentBuilder db = dbf.newDocumentBuilder();
                        Document doc = db.newDocument();
                        Element customXml = doc.createElement("Map");
                        for (Map.Entry<?, ?> entry : map.entrySet()) {
                                Element keyValuePair = doc.createElement(entry.getKey()
                                                .toString());
                                keyValuePair.appendChild(doc.createTextNode(entry.getValue()
                                                .toString()));
                                customXml.appendChild(keyValuePair);
                        }
                        return customXml;
                } catch (Exception e) {
                        e.printStackTrace();
                }

                return null;
        }
}

With this approach you only need one additional JAXB-annotation to achieve
the required affect:
@XmlJavaTypeAdapter(JsonMapAdapter.class)

Best,
  Leo

-- 
View this message in context: http://old.nabble.com/Custom-mapping-for-Maps-in-JAXB-tp28779221p28945407.html
Sent from the java.net - jaxb users mailing list archive at Nabble.com.