whoami
whoami

Reputation: 1627

JAXB Map adapter

I've problem in converting XML complex type to java.util.Map. While unmarshalling, only the value is getting populated and key becomes NULL. I want the below XML to be converted as java.util.Map<"user_type","students"> but its coming like java.util.Map<NULL,"students">. Can someone please tell why I'm getting the key as NULL?

XML

<root>
  <myMap>
    <user_type>students</user_type>
  </myMap>
</root>

POJO

@XmlRootElement(name = "root")
@XmlAccessorType(XmlAccessType.FIELD)
public class MyPojo implements Serializable {

private static final long serialVersionUID = -4589166768649033266L;

@XmlElement(name = "myMap")
@XmlJavaTypeAdapter(MapAdapter.class)
private Map<String,String> myMap;

 //getters and setters
}

MapAdapter implementation is http://blog.bdoughan.com/2013/06/moxys-xmlvariablenode-using-maps-key-as.html

@Override
public Map<String, String> unmarshal(AdaptedMap adaptedMap) throws Exception {
    List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
    Map<String, String> map = new HashMap<String, String>(adaptedEntries.size());
    for(AdaptedEntry adaptedEntry : adaptedEntries) {
        map.put(adaptedEntry.key, adaptedEntry.value);
    }
    return map;
}

Upvotes: 2

Views: 4603

Answers (2)

Jason Xavier
Jason Xavier

Reputation: 63

There are 2 ways of doing this if you use the Conversion Box ... (http://capsulesforthejavamind.blogspot.in/2015/01/conversion-box.html)

Way 1

Convert the XML to a Map and set the Map into the POJO.

import java.util.Map;

import cjm.component.cb.map.ToMap;

public class Testing
{
public static void main(String[] args)
{
    try
    {
        String xml = "<root><myMap><user_type>students</user_type></myMap></root>";

        System.out.println("XML: " + xml); // your XML

        ToMap toMap = new ToMap();

        Map<String, Object> map = toMap.convertToMap(xml);

        System.out.println("Converted Map: " + map); // converted map

        Map<String, Object> rootMap = (Map<String, Object>) map.get("root");
        Map<String, String> myMap = (Map<String, String>) rootMap.get("myMap");

        System.out.println("My Map: " + myMap); // final map you want);

        MyPojo myPojo = new MyPojo();

        myPojo.setMyMap(myMap); // map set to the POJO

        System.out.println("POJO: " + myPojo);
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
}

Output:

XML: <root><myMap><user_type>students</user_type></myMap></root>
-------- XML Detected -------- 
-------- Map created Successfully -------- 
Converted Map: {root={myMap={user_type=students}}}
My Map: {user_type=students}
POJO: MyPojo [myMap={user_type=students}]

Way 2

Convert the XML into the POJO. But then this requires the POJO to be exactly as per the structure of the XML. Thus I have created a Root class as follows:

public class Root
{
private MyPojo myPojo;

public MyPojo getMyPojo()
{
    return myPojo;
}

public void setMyPojo(MyPojo myPojo)
{
    this.myPojo = myPojo;
}

@Override
public String toString()
{
    return "Root [myPojo=" + myPojo + "]";
}
}

My Pojo

import java.io.Serializable;
import java.util.Map;

public class MyPojo implements Serializable
{
private static final long serialVersionUID = -4589166768649033266L;

private Map<String, String> myMap;

public Map<String, String> getMyMap()
{
    return myMap;
}

public void setMyMap(Map<String, String> myMap)
{
    this.myMap = myMap;
}

@Override
public String toString()
{
    return "MyPojo [myMap=" + myMap + "]";
}
}

Conversion

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import cjm.component.cb.object.ToObject;

public class Testing
{
public static void main(String[] args)
{
    try
    {
        String xml = "<root><myMap><user_type>students</user_type></myMap></root>";

        System.out.println("XML: " + xml); // your XML

        Map<String, String> headerMap = new HashMap<String, String>();

        headerMap.put("root", "myPojo"); // header substitutions

        List<Object> objectList = new ArrayList<Object>();

        objectList.add(new MyPojo()); // since its a nested POJO

        ToObject toObject = new ToObject();

        Root root = (Root) toObject.convertToObject(xml, new Root(), objectList, headerMap); // conversion to POJO

        System.out.println("POJO: " + root);
    }
    catch (Exception e)
    {
        e.printStackTrace();
    }
}
}

Output:

XML: <root><myMap><user_type>students</user_type></myMap></root>
-------- XML Detected -------- 
-------- XML Detected -------- 
-------- Map created Successfully -------- 
-------- Object created Successfully -------- 
POJO: Root [myPojo=MyPojo [myMap={user_type=students}]]

Upvotes: 1

Buhake Sindi
Buhake Sindi

Reputation: 89189

The first mistake I see is that you are using a Map<String, Integer> mapping whereas your XML has a string value.

Change your code as follows:

public class MapAdapter extends XmlAdapter<MapAdapter.AdaptedMap, Map<String, String>> {

    public static class AdaptedMap {

        @XmlVariableNode("key")
        List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();

    }

    public static class AdaptedEntry {

        @XmlTransient
        public String key;

        @XmlValue
        public String value;

    }

    @Override
    public AdaptedMap marshal(Map<String, String> map) throws Exception {
        AdaptedMap adaptedMap = new AdaptedMap();
        for(Entry<String, String> entry : map.entrySet()) {
            AdaptedEntry adaptedEntry = new AdaptedEntry();
            adaptedEntry.key = entry.getKey();
            adaptedEntry.value = entry.getValue();
            adaptedMap.entries.add(adaptedEntry);
        }
        return adaptedMap;
    }

    @Override
    public Map<String, String> unmarshal(AdaptedMap adaptedMap) throws Exception {
        List<AdaptedEntry> adaptedEntries = adaptedMap.entries;
        Map<String, String> map = new HashMap<String, String>(adaptedEntries.size());
        for(AdaptedEntry adaptedEntry : adaptedEntries) {
            map.put(adaptedEntry.key, adaptedEntry.value);
        }
        return map;
    }

}

In your MyPojo class, you need to specify which XML element you will need to map as a Map.

@XmlElement(name = "myMap"),
@XmlJavaTypeAdapter(MapAdapter.class)
private Map<String,String> myMap;

Upvotes: 1

Related Questions