Daniel  Dudarev Daniel Dudarev - 3 months ago 53
JSON Question

JAXB mapping Map with complex object to JSON

I have a problem with serializing to JSON my complex object, which contains Map with another complex objects. I'm using JAXB and MOXy. Here are my classes:
Root class which i want to serialize/deserialize:

@XmlRootElement
@XmlSeeAlso(ComplexBean.class)
@XmlAccessorType(XmlAccessType.FIELD)
public class MapBean implements Serializable {

@XmlJavaTypeAdapter(MapAdapter.class)
@XmlElement
private Map<String, ComplexBean> complexBeans;


My ComplexBean class (getters/setters are omitted):

public class ComplexBean implements Serializable {
private String name;
private String surName;
private double weight;


MapAdapter class:

public class MapAdapter extends XmlAdapter<Element, Map<String, ComplexBean>> {

private DocumentBuilder documentBuilder;

public MapAdapter() throws Exception {
documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
}

public Element marshal(Map<String, ComplexBean> map) throws Exception {
Document document = documentBuilder.newDocument();
Element rootElement = document.createElement("complexBeans");
document.appendChild(rootElement);
for(Map.Entry<String, ComplexBean> entry : map.entrySet()) {
Element childElement = document.createElement(entry.getKey());
ComplexBean complexBean = entry.getValue();

Attr nameAttribute = document.createAttribute("name");
nameAttribute.setValue(complexBean.getName());
childElement.setAttributeNode(nameAttribute);

Attr surNameAttribute = document.createAttribute("surName");
surNameAttribute.setValue(complexBean.getSurName());
childElement.setAttributeNode(surNameAttribute);

Attr weightAttribute = document.createAttribute("weight");
weightAttribute.setValue(String.valueOf(complexBean.getWeight()));
childElement.setAttributeNode(weightAttribute);

rootElement.appendChild(childElement);
}
return rootElement;
}

public Map<String, ComplexBean> unmarshal(Element element) throws Exception {
System.out.println("In unmarshaller");
Map<String, ComplexBean> complexBeans = new HashMap<String, ComplexBean>();
return null;
}
}


And My main method:

public static void main(String[] args) throws Exception {
Map<String, Object> jaxbProperties = new HashMap<String, Object>();
jaxbProperties.put(JAXBContextProperties.MEDIA_TYPE, MediaType.APPLICATION_JSON);
jaxbProperties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
javax.xml.bind.JAXBContext jc = JAXBContext.newInstance(new Class[] {MapBean.class}, jaxbProperties);

Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

MapBean mapBean = getMapBean();

Writer writer = new StringWriter();
marshaller.marshal(mapBean, writer);
String json = writer.toString();
System.out.println("Output JSON: ");
System.out.println(json);

StreamSource streamSource = new StreamSource(json);
Unmarshaller unmarshaller = jc.createUnmarshaller();
MapBean unmarshalledMapBean = unmarshaller.unmarshal(streamSource, MapBean.class).getValue();
System.out.println(unmarshalledMapBean);
}


So, when I run my main method, marshaller returns such json:

{
"complexBeans" : {
"first" : {
"name" : "cbName1",
"surName" : "cbSurName1",
"weight" : "50.5"
},
"second" : {
"name" : "cbName2",
"surName" : "cbSurName2",
"weight" : "10.5"
}
}
}


But umarshaller doesn't call unmarshall method in MapAdapter class and fails with exception:

Caused by: Exception [EclipseLink-25004] (Eclipse Persistence Services - 2.6.1.v20150916-55dc7c3): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: java.net.MalformedURLException: no protocol: {
"complexBeans" : {
"first" : {
"name" : "cbName1",
"surName" : "cbSurName1",
"weight" : "50.5"
},
"second" : {
"name" : "cbName2",
"surName" : "cbSurName2",
"weight" : "10.5"
}
}
}
at org.eclipse.persistence.exceptions.XMLMarshalException.unmarshalException(XMLMarshalException.java:120)
at org.eclipse.persistence.internal.oxm.record.json.JsonStructureReader.parse(JsonStructureReader.java:156)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:863)
at org.eclipse.persistence.internal.oxm.record.SAXUnmarshaller.unmarshal(SAXUnmarshaller.java:710)
at org.eclipse.persistence.internal.oxm.XMLUnmarshaller.unmarshal(XMLUnmarshaller.java:643)
at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:339)
... 1 more
at java.net.URL.<init>(URL.java:586)
at java.net.URL.<init>(URL.java:483)
at java.net.URL.<init>(URL.java:432)
at org.eclipse.persistence.internal.oxm.record.json.JsonStructureReader.parse(JsonStructureReader.java:127)
at org.eclipse.persistence.internal.oxm.record.json.JsonStructureReader.parse(JsonStructureReader.java:154)
... 5 more


So, please, help me to unmarshal my json to MapBean object. What is my mistake?

Answer

Your StreamSource call is wrong in main method.You are directly passing json string to StreamSource constructor.

this is what you have currently--

StreamSource streamSource = new StreamSource(String json);

You should have something like below as per StreamSource specification

StreamSource streamSource = new StreamSource("file location/URL");

Or

StreamSource streamSource = new StreamSource(File);