Reputation: 125
I know there are similar questions around such as How to marshal/unmarshal a Map<Integer, List<Integer>>? and JAXB java.util.Map binding. Also I read Blaise Doughan's blog a lot especially this post: http://blog.bdoughan.com/2013/03/jaxb-and-javautilmap.html and tried to follow what he suggested as much as I can, however I still cannot unmarshal the json payload successfully and really appreciate your help.
The json payload to unmarshal looks like this:
{
"uri":"\\foo\\dosomthing",
"action":"POST",
"queryParameters":[
"$filter=aaa",
"$orderby=bbb"
],
"requestData":{
"data1":{
"key1":"value1",
"key2":"value2"
},
"ids":[
"1234",
"0294"
]
}
}
And I am having problem to unmarshal the "data" into the java.util.Map. The "data" field does not have specific schema so it can contains an array, key-value pairs or any other valid json data. I decided to use a Map to wrap it. Based on what I researched, I think I need XmlAdapter to convert the data properly.
Here are my code:
The Java Schema Class:
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlPath;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class CustomerRequest
{
public CustomerRequest() {}
public CustomerRequest(String uri, String action, List<String>
queryParameters, Map<String, Object> reqeustData)
{
this.uri = uri;
this.action = action;
this.queryParameters = queryParameters;
this.requestData = reqeustData;
}
public String getUri()
{
return uri;
}
public String getAction()
{
return action;
}
public List<String> getQueryParameters()
{
return Collections.unmodifiableList(queryParameters);
}
public Map<String, Object> getRequestData()
{
return Collections.unmodifiableMap(requestData);
}
@XmlElement
private String uri;
@XmlElement
private String action;
@XmlElementWrapper
private List<String> queryParameters = new ArrayList<String>();
@XmlPath(".")
@XmlJavaTypeAdapter(StringObjectMapAdapter.class)
private Map<String, Object> requestData = new HashMap<String, Object>();
}
The XmlAdpater:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlValue;
import javax.xml.bind.annotation.adapters.XmlAdapter;
import org.eclipse.persistence.oxm.annotations.XmlVariableNode;
public class StringObjectMapAdapter extends
XmlAdapter<StringObjectMapAdapter.AdaptedMap, Map<String, Object>>
{
public static class AdaptedEntry
{
@XmlTransient
public String key;
@XmlValue
public Object value = new Object();
}
public static class AdaptedMap
{
@XmlVariableNode("key")
List<AdaptedEntry> entries = new ArrayList<AdaptedEntry>();
}
@Override
public AdaptedMap marshal(Map<String, Object> map) throws Exception
{
AdaptedMap adaptedMap = new AdaptedMap();
for (Entry<String, Object> 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, Object> unmarshal(AdaptedMap adaptedMap) throws Exception
{
List<AdaptedEntry> adapatedEntries = adaptedMap.entries;
Map<String, Object> map = new HashMap<String, Object>(adapatedEntries.size());
for (AdaptedEntry adaptedEntry : adapatedEntries )
{
map.put(adaptedEntry.key, adaptedEntry.value);
}
return map;
}
}
and finally is my test app:
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.testng.annotations.Test;
public class TestStringObjectMapAdapter {
@Test
public void testUnmarshalFromJson() throws Exception
{
JAXBContext jc = JAXBContext.newInstance(CustomerRequest.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty(MarshallerProperties.MEDIA_TYPE, "application/json");
unmarshaller.setProperty(MarshallerProperties.JSON_INCLUDE_ROOT, false);
StreamSource json = new StreamSource("test-data.json");
CustomerRequest request= unmarshaller.unmarshal(json,
CustomerRequest.class).getValue();
assert(request.getUri().equals("\\foo\\dosomthing"));
assert(request.getAction().equals("POST"));
}
}
Then when test app runs, an java.lang.ClassCastException exception is generated:
FAILED: testUnmarshalFromJson
java.lang.ClassCastException: com.sun.org.apache.xerces.internal.dom.DocumentImpl cannot be cast to org.w3c.dom.Element
at org.eclipse.persistence.internal.oxm.XMLCompositeObjectMappingNodeValue.endSelfNodeValue(XMLCompositeObjectMappingNodeValue.java:468)
at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.endDocument(UnmarshalRecordImpl.java:606)
at org.eclipse.persistence.internal.oxm.record.UnmarshalRecordImpl.endElement(UnmarshalRecordImpl.java:1084)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:304)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parseRoot(JSONReader.java:179)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:125)
at org.eclipse.persistence.internal.oxm.record.json.JSONReader.parse(JSONReader.java:140)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:857)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:707)
at org.eclipse.persistence.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:655)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:301)
at com.absolute.asb.urp.services.domain.TestStringObjectMapAdapter.testUnmarshalFromJson(TestStringObjectMapAdapter.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
Upvotes: 1
Views: 2945
Reputation: 169
Maybe you should try creating the correct MoXY JAXBContext like:
private static synchronized JAXBContext createJAXBContext() throws JAXBException {
if(jc == null){
jc = org.eclipse.persistence.jaxb.JAXBContextFactory.createContext(new Class[] {CustomerReqeust.class}, null);
}
return jc;
}
Or use another way like mentioned in http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
Btw "CustomerReqeust" is a little bit wrong spelled :-)
Upvotes: 1