I have this JSON-Content:
{"color":null}
And I want to make these Java-Objects out of it (and vice-versa):
Container
|- List<Entry> entries
|- Entry
|- String key = "color"
|- String value = null
My current solution always deserializes "color":null to an empty String. I found other solutions, that will instead deserialize null or empty String to null.
How can I get MOXy (or any other jaxb implementation) to deserialize null as null and empty Strings to empty Strings?
I am using this code:
import java.io.ByteArrayOutputStream;
import java.util.*;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.internal.oxm.ByteArraySource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.oxm.annotations.*;
import org.junit.*;
class Container {
#XmlVariableNode("key")
List<Entry> entries = new ArrayList<Entry>();
}
class Entry {
#XmlTransient
public String key;
#XmlValue
#XmlNullPolicy(nullRepresentationForXml=XmlMarshalNullRepresentation.XSI_NIL, xsiNilRepresentsNull=false)
public String value;
}
public class D {
/** THIS TEST FAILS!!! */
#Test
public void unmarshallNull() throws Exception {
Assert.assertEquals(null, unmarshall("xml", "<root><color xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/></root>"));
Assert.assertEquals(null, unmarshall("json", "{\"color\":null}"));
}
/* All other tests are passing. */
#Test
public void unmarshallEmpty() throws Exception {
Assert.assertEquals("", unmarshall("xml", "<root><color></color></root>"));
Assert.assertEquals("", unmarshall("json", "{\"color\":\"\"}"));
}
#Test
public void unmarshallValue() throws Exception {
Assert.assertEquals("red", unmarshall("xml", "<root><color>red</color></root>"));
Assert.assertEquals("red", unmarshall("json", "{\"color\":\"red\"}"));
}
#Test
public void marshallNull() throws Exception {
Assert.assertEquals("<color xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/>", marshall("xml", null));
Assert.assertEquals("{\"color\":null}", marshall("json", null));
}
#Test
public void marshallEmpty() throws Exception {
Assert.assertEquals("<color></color>", marshall("xml", ""));
Assert.assertEquals("{\"color\":\"\"}", marshall("json", ""));
}
#Test
public void marshallValue() throws Exception {
Assert.assertEquals("<color>red</color>", marshall("xml", "red"));
Assert.assertEquals("{\"color\":\"red\"}", marshall("json", "red"));
}
private static String marshall(String format, String value) throws JAXBException {
// prepare
JAXBContext jc = JAXBContext.newInstance(Container.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/"+format);
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
marshaller.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
// define example data
Container detail = new Container();
Entry entry = new Entry();
entry.key = "color";
entry.value = value;
detail.entries.add(entry);
// marshall
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
marshaller.marshal(detail, outputStream);
return outputStream.toString();
}
private static String unmarshall(String format, String raw) throws JAXBException {
// prepare
JAXBContext jc = JAXBContext.newInstance(Container.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/"+format);
unmarshaller.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
// unmarshall
Container container = unmarshaller.unmarshal(new ByteArraySource(raw.getBytes()), Container.class).getValue();
return container.entries.get(0).value;
}
}
It works for XML but fails for JSON:
Test failure: unmarshallNull
java.lang.AssertionError: expected:<null> but was:<>
I also configured MOXy as my jaxb-provider for the relevant package (jaxb.properties):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
I'm using MOXy 2.22.1 and Java8 (but same behaviour with 2.18, 2.19, 2.20, 2.21, 2.22). My Maven pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>x</groupId>
<artifactId>x</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
<version>2.22.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
There are things you might want to try.
First, try using #XmlElement(nillable=true) instead of XmlNullPolicy annotation or setting the emptyNodeRepresentsNull parameter.
Second, you might want to write your own XmlAdapter which will do pretty much the same thing - unmarshall empty string to null. The main difference is that you can also manually configure which fields will be unmarshalled with your adapter and which will not, therefore retaining the current behaviour. Please refer to this answer by Blaise Doughan to see how do you wire a custom XmlAdapter to your configuration.
Third, as far as I know Jackson doesn't have this problem. See this answer ot this wiki page about how to set up a custom deserializer for a field in case it does.
Related
I have a simple directed graph from jgrapht and I am trying to serialize it into a JSON file using jackson as follows:
ObjectMapper mapper = new ObjectMapper();
File output = new File("P:\\tree.json");
ObjectWriter objectWriter = mapper.writer().withDefaultPrettyPrinter();
objectWriter.writeValue(output,simpleDirectedGraph);
However I get this error:
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class org.jgrapht.graph.AbstractBaseGraph$ArrayListFactory and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: org.jgrapht.graph.SimpleDirectedGraph["edgeSetFactory"])
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32)
at com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField(BeanPropertyWriter.java:693)
at com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields(BeanSerializerBase.java:675)
at com.fasterxml.jackson.databind.ser.BeanSerializer.serialize(BeanSerializer.java:157)
at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
at com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize(ObjectWriter.java:1387)
at com.fasterxml.jackson.databind.ObjectWriter._configAndWriteValue(ObjectWriter.java:1088)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:909)
at ms.fragment.JSONTreeGenerator.main(JSONTreeGenerator.java:45)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
I have seen that there is a GmlExporter but I am interested in json... how can I do that?
You can disable the exception with:
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
The exception you got from Jackson:
JsonMappingException: No serializer found for class org.jgrapht.graph.AbstractBaseGraph$ArrayListFactory
and no properties discovered to create BeanSerializer
(to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)) (through reference chain:
org.jgrapht.graph.SimpleDirectedGraph["edgeSetFactory"])
at com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
gives a clue on how to solve:
no properties discovered to create BeanSerializer
property SimpleDirectedGraph["edgeSetFactory"] seems empty
Exclude the type of property edgeSetFactory from serialisation:
AbstractBaseGraph$ArrayListFactory
Write a custom Serializer
Usually Jackson would use a StdBeanSerializer to write any non-primitive class to JSON.
Unfortunately this does not work for abstract classes.
So you can write your own JsonSerializer to handle special fields.
You can serialize your Graph to XML and then from XML to JSON :
Serialization to XML : you can use this Library: XStream http://x-stream.github.io it's a small library that will easily allow you to serialize and deserialize to and from XML.
guide to use the Library : http://x-stream.github.io/tutorial.html
After that try to map your XML to JSON using :
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20180813</version>
</dependency>
XML.java is the class you're looking for:
import org.json.JSONObject;
import org.json.XML;
import org.json.JSONException;
public class Main {
public static int PRETTY_PRINT_INDENT_FACTOR = 4;
public static String TEST_XML_STRING =
"<?xml version=\"1.0\" ?><test attrib=\"moretest\">Turn this to JSON</test>";
public static void main(String[] args) {
try {
JSONObject xmlJSONObj = XML.toJSONObject(TEST_XML_STRING);
String jsonPrettyPrintString = xmlJSONObj.toString(PRETTY_PRINT_INDENT_FACTOR);
System.out.println(jsonPrettyPrintString);
} catch (JSONException je) {
System.out.println(je.toString());
}
}
}
Option 1
Newer versions of JGraphT have built-in support for importing/exporting graphs from/to JSON using the jgrapht-io
module.
Here's an example for exporting a graph to JSON:
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.nio.json.JSONExporter;
import java.net.URI;
import java.net.URISyntaxException;
public class Main {
public static void main(String[] args) throws Exception {
final var jsonExporter = new JSONExporter<URI, DefaultEdge>();
jsonExporter.exportGraph(
newSampleGraph(),
System.out
);
System.out.println("");
}
// Copied from https://jgrapht.org/guide/HelloJGraphT
private static Graph<URI, DefaultEdge> newSampleGraph() throws URISyntaxException {
Graph<URI, DefaultEdge> g = new DefaultDirectedGraph<>(DefaultEdge.class);
URI google = new URI("http://www.google.com");
URI wikipedia = new URI("http://www.wikipedia.org");
URI jgrapht = new URI("http://www.jgrapht.org");
// add the vertices
g.addVertex(google);
g.addVertex(wikipedia);
g.addVertex(jgrapht);
// add edges to create linking structure
g.addEdge(jgrapht, wikipedia);
g.addEdge(google, jgrapht);
g.addEdge(google, wikipedia);
g.addEdge(wikipedia, google);
return g;
}
}
The pom.xml file for reference:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.stackoverflow</groupId>
<artifactId>questions-39438962</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-core</artifactId>
<version>1.5.1</version>
</dependency>
<dependency>
<groupId>org.jgrapht</groupId>
<artifactId>jgrapht-io</artifactId>
<version>1.5.1</version>
</dependency>
</dependencies>
</project>
Option 2
Implement a custom Jackson serializer for JGraphT graphs, register it with an ObjectMapper, and implement the logic in the serializer.
Here's an example:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;
import org.jgrapht.Graph;
import org.jgrapht.nio.IntegerIdProvider;
import java.io.IOException;
public class DefaultDirectedGraphSerializer<V, E, T extends Graph<V, E>> extends StdSerializer<T> {
public DefaultDirectedGraphSerializer(Class<T> t) {
super(t);
}
public DefaultDirectedGraphSerializer() {
this(null);
}
#Override
public void serialize(T value, JsonGenerator gen, SerializerProvider provider) throws IOException {
final var idProvider = new IntegerIdProvider<>();
gen.writeStartObject();
gen.writeFieldName("graph");
gen.writeStartObject();
gen.writeFieldName("nodes");
gen.writeStartObject();
for (V v : value.vertexSet()) {
final var id = idProvider.apply(v);
gen.writeFieldName(id);
gen.writeStartObject();
gen.writeStringField("label", v.toString());
gen.writeEndObject();
}
gen.writeEndObject();
gen.writeFieldName("edges");
gen.writeStartArray();
for (E e : value.edgeSet()) {
gen.writeStartObject();
final var source = value.getEdgeSource(e);
final var target = value.getEdgeTarget(e);
gen.writeStringField("source", idProvider.apply(source));
gen.writeStringField("target", idProvider.apply(target));
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeEndObject();
gen.writeEndObject();
}
}
import org.jgrapht.Graph;
import org.jgrapht.graph.DefaultDirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import java.net.URI;
import java.net.URISyntaxException;
public class Graphs {
public static Graph<URI, DefaultEdge> newSampleGraph() throws URISyntaxException {
final var g = new DefaultDirectedGraph<URI, DefaultEdge>(DefaultEdge.class);
URI google = new URI("http://www.google.com");
URI wikipedia = new URI("http://www.wikipedia.org");
URI jgrapht = new URI("http://www.jgrapht.org");
g.addVertex(google);
g.addVertex(wikipedia);
g.addVertex(jgrapht);
g.addEdge(jgrapht, wikipedia);
g.addEdge(google, jgrapht);
g.addEdge(google, wikipedia);
g.addEdge(wikipedia, google);
return g;
}
}
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.jgrapht.graph.DefaultDirectedGraph;
import java.net.URISyntaxException;
import static org.example.Graphs.newSampleGraph;
public class Main {
public static void main(String[] args) throws URISyntaxException, JsonProcessingException {
final var module = new SimpleModule();
module.addSerializer(DefaultDirectedGraph.class, new DefaultDirectedGraphSerializer<>());
final ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(module);
System.out.println(mapper.writeValueAsString(newSampleGraph()));
}
}
This will produce the following JSON document (after pretty printing):
{
"graph": {
"nodes": {
"1": {
"label": "http://www.google.com"
},
"2": {
"label": "http://www.wikipedia.org"
},
"3": {
"label": "http://www.jgrapht.org"
}
},
"edges": [
{
"source": "3",
"target": "2"
},
{
"source": "1",
"target": "3"
},
{
"source": "1",
"target": "2"
},
{
"source": "2",
"target": "1"
}
]
}
}
import org.jgrapht.Graph;
import org.jgrapht.io.JSONExporter;
import org.jgrapht.io.SimpleGraphExporter;
import org.jgrapht.io.SimpleGraphImporter;
import org.jgrapht.io.JSONImporter;
import org.jgrapht.io.JSONExporter;
import org.jgrapht.io.ExportException;
import org.jgrapht.io.ImportException;
// Create a new, empty simple graph
Graph<String, DefaultEdge> graph = new SimpleGraph<>(DefaultEdge.class);
// Add some vertices and edges to the graph
graph.addVertex("A");
graph.addVertex("B");
graph.addVertex("C");
graph.addEdge("A", "B");
graph.addEdge("B", "C");
// Create a new JSONExporter instance
JSONExporter<String, DefaultEdge> exporter = new JSONExporter<>();
// Export the graph to JSON
String json = exporter.toJson(graph);
Consider this example -
I have a class called Report that has a field of type Message. The Message class has a field called "body" which is a string. "body" can be any string, but sometimes it contains properly formatted XML content. How can I ensure that when the "body" contains XML content, the serialization takes the form of an XML structure rather than what it gives at present?
Here is the code with the output -
Report class
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
#XmlRootElement(name = "Report")
#XmlType(propOrder = { "message"})
public class Report
{
private Message message;
public Message getMessage() { return message; }
public void setMessage(Message m) { message = m; }
}
Message class
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
#XmlType(propOrder = { "body" })
public class Message
{
private String body;
public String getBody() { return body; }
#XmlElement
public void setBody(String body) { this.body = body; }
}
Main
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class SerializationTest
{
public static void main(String args[]) throws Exception
{
JAXBContext jaxbContext = JAXBContext.newInstance(Report.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Report report = new Report();
Message message = new Message();
message.setBody("Sample report message.");
report.setMessage(message);
jaxbMarshaller.marshal(report, System.out);
message.setBody("<rootTag><body>All systems online.</body></rootTag>");
report.setMessage(message);
jaxbMarshaller.marshal(report, System.out);
}
}
The output is as follows -
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Report>
<message>
<body>Sample report message.</body>
</message>
</Report>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Report>
<message>
<body><rootTag><body>All systems online.</body></rootTag></body>
</message>
</Report>
As you can see in the above output, for the second instance of "body", the serialization produced
<body><rootTag><body>All systems online.</body></rootTag></body>
instead of
<body><rootTag><body>All systems online.</body></rootTag></body>
How to solve this problem?
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
This use case is mapped using the #XmlAnyElement annotation and specifying a DOMHandler. There appears to be bug when doing this with the JAXB RI, but the following use case works with EclipseLink JAXB (MOXy).
BodyDomHandler
By default a JAXB impleemntation will represent unmapped content as a DOM node. You can leverage a DomHandler to an alternate representation of the DOM, In this case we will represent the DOM as a String.
import java.io.*;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.annotation.DomHandler;
import javax.xml.transform.Source;
import javax.xml.transform.stream.*;
public class BodyDomHandler implements DomHandler<String, StreamResult> {
private static final String BODY_START_TAG = "<body>";
private static final String BODY_END_TAG = "</body>";
private StringWriter xmlWriter = new StringWriter();
public StreamResult createUnmarshaller(ValidationEventHandler errorHandler) {
return new StreamResult(xmlWriter);
}
public String getElement(StreamResult rt) {
String xml = rt.getWriter().toString();
int beginIndex = xml.indexOf(BODY_START_TAG) + BODY_START_TAG.length();
int endIndex = xml.indexOf(BODY_END_TAG);
return xml.substring(beginIndex, endIndex);
}
public Source marshal(String n, ValidationEventHandler errorHandler) {
try {
String xml = BODY_START_TAG + n.trim() + BODY_END_TAG;
StringReader xmlReader = new StringReader(xml);
return new StreamSource(xmlReader);
} catch(Exception e) {
throw new RuntimeException(e);
}
}
}
Message
Below is how you would specify the #XmlAnyElement annotation on your Message class.
import javax.xml.bind.annotation.XmlAnyElement;
import javax.xml.bind.annotation.XmlType;
#XmlType(propOrder = { "body" })
public class Message
{
private String body;
public String getBody() { return body; }
#XmlAnyElement(BodyDomHandler.class)
public void setBody(String body) { this.body = body; }
}
Output
Below is the output from running your SerialziationTest:
<?xml version="1.0" encoding="UTF-8"?>
<Report>
<message>
<body>Sample report message.</body>
</message>
</Report>
<?xml version="1.0" encoding="UTF-8"?>
<Report>
<message>
<body>
<rootTag>
<body>All systems online.</body>
</rootTag>
</body>
</message>
</Report>
For More Information
http://blog.bdoughan.com/2011/04/xmlanyelement-and-non-dom-properties.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
NOTE - Bug in JAXB RI
There appears to be a bug in the JAXB reference implementation, and the example code will result in a stack trace like the following:
Exception in thread "main" javax.xml.bind.MarshalException
- with linked exception:
[com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an #XmlRootElement annotation]
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:317)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:243)
at javax.xml.bind.helpers.AbstractMarshallerImpl.marshal(AbstractMarshallerImpl.java:75)
at forum12428727.SerializationTest.main(SerializationTest.java:20)
Caused by: com.sun.istack.internal.SAXException2: unable to marshal type "java.lang.String" as an element because it is missing an #XmlRootElement annotation
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.reportError(XMLSerializer.java:216)
at com.sun.xml.internal.bind.v2.runtime.LeafBeanInfoImpl.serializeRoot(LeafBeanInfoImpl.java:126)
at com.sun.xml.internal.bind.v2.runtime.property.SingleReferenceNodeProperty.serializeBody(SingleReferenceNodeProperty.java:100)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:306)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsXsiType(XMLSerializer.java:664)
at com.sun.xml.internal.bind.v2.runtime.property.SingleElementNodeProperty.serializeBody(SingleElementNodeProperty.java:141)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeBody(ClassBeanInfoImpl.java:306)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsSoleContent(XMLSerializer.java:561)
at com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl.serializeRoot(ClassBeanInfoImpl.java:290)
at com.sun.xml.internal.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:462)
at com.sun.xml.internal.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:314)
... 3 more
If its only for Marshalling, and to ignore the < and >,
We can use the following:
marshaller.setProperty("com.sun.xml.bind.marshaller.CharacterEscapeHandler",
new CharacterEscapeHandler() {
#Override
public void escape(char[] ac, int i, int j, boolean flag,
Writer writer) throws IOException {
writer.write(ac, i, j);
}
});
3 different solutions 1), 2) 3), here below :
1) Following post is a the description of your solution Loresh :
http://anna-safronova.livejournal.com/2524.html?thread=9180
This is still missing limitations details.
With embeeded html, we need a <![CDATA block
JAXB's dependancy
com.sun.xml.bind.marshaller.CharacterEscapeHandler
Needs import jaxb-impl for compilation / and may be required for excution, e.g.
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.2.4</version>
Limitation : this solution is Container-specific and may not run because of class-loading policy.
2) Another similar approach is JDK's rt.jar dependancy
com.sun.xml.internal.bind.CharacterEscapeHandler
http://theopentutorials.com/tutorials/java/jaxb/jaxb-marshalling-and-unmarshalling-cdata-block/
Same limitation / dependends on target JDK, and some tweaks on Eclipse/Maven are necessary (bad alternative / My opinion)
3) Finally, the best solution was found on another post of Reg Whitton :
https://stackoverflow.com/a/12637295/560410
and this is the detailed reciepe :
http://javacoalface.blogspot.co.uk/2012/09/outputting-cdata-sections-with-jaxb.html
Worked perfect for me !
I am new to JAXB and would like to know if there is a way by which I can unmarshall an XML to my response object but using xpath expressions. The issue is that I am calling a third party webservice and the response which I receive has a lot of details. I do not wish to map all the details in the XML to my response object. I just wish to map few details from the xml using which I can get using specific XPath expressions and map those to my response object. Is there an annotation which can help me achieve this?
For example consider the following response
<root>
<record>
<id>1</id>
<name>Ian</name>
<AddressDetails>
<street> M G Road </street>
</AddressDetails>
</record>
</root>
I am only intrested in retrieving the street name so I want to use xpath expression to get value of street using 'root/record/AddressDetails/street' and map it to my response object
public class Response{
// How do i map this in jaxb, I do not wish to map record,id or name elements
String street;
//getter and setters
....
}
Thanks
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JST-222) expert group.
You could use MOXy's #XmlPath extension for this use case.
Response
import javax.xml.bind.annotation.*;
import org.eclipse.persistence.oxm.annotations.XmlPath;
#XmlRootElement(name="root")
#XmlAccessorType(XmlAccessType.FIELD)
public class Response{
#XmlPath("record/AddressDetails/street/text()")
String street;
//getter and setters
}
jaxb.properties
To use MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html)
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("src/forum17141154/input.xml");
Response response = (Response) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<root>
<record>
<AddressDetails>
<street> M G Road </street>
</AddressDetails>
</record>
</root>
For More Information
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html
http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html
If all you want is the street name, just use an XPath expression to get it as a string, and forget about JAXB - the complex JAXB machinery is not adding any value.
import javax.xml.xpath.*;
import org.xml.sax.InputSource;
public class XPathDemo {
public static void main(String[] args) throws Exception {
XPathFactory xpf = XPathFactory.newInstance();
XPath xpath = xpf.newXPath();
InputSource xml = new InputSource("src/forum17141154/input.xml");
String result = (String) xpath.evaluate("/root/record/AddressDetails/street", xml, XPathConstants.STRING);
System.out.println(result);
}
}
If you want to map to the middle of an XML document you could do the following:
Demo Code
You could use a StAX parser to advance to the part of the document you wish to unmarshal, and then unmarshal the XMLStreamReader from there,.
import javax.xml.bind.*;
import javax.xml.stream.*;
import javax.xml.transform.stream.StreamSource;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Response.class);
XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource xml = new StreamSource("src/forum17141154/input.xml");
XMLStreamReader xsr = xif.createXMLStreamReader(xml);
while(!(xsr.isStartElement() && xsr.getLocalName().equals("AddressDetails"))) {
xsr.next();
}
Unmarshaller unmarshaller = jc.createUnmarshaller();
JAXBElement<Response> response = unmarshaller.unmarshal(xsr, Response.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
}
Response
The mappings on the domain object are made relative to the fragment of the XML document you are mapping to.
import javax.xml.bind.annotation.*;
#XmlAccessorType(XmlAccessType.FIELD)
public class Response{
String street;
//getter and setters
}
Output
<?xml version="1.0" encoding="UTF-8"?>
<AddressDetails>
<street> M G Road </street>
</AddressDetails>
For More Information
http://blog.bdoughan.com/2012/08/handle-middle-of-xml-document-with-jaxb.html
#Xpath of Moxy works like a charm. But I got only null values in the beginning. Seems that it works a bit different with java 11 and above:
For me this post was helpful: #XmlPath has no impact during the JAXB Marshalling .
It finally worked with using these dependencies:
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>eclipselink</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>jakarta.activation</groupId>
<artifactId>jakarta.activation-api</artifactId>
<version>2.0.1</version>
</dependency>
This in de jaxb.properties file (notice 'jakarta', this is different for older versions of java: jakarta.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Put this jaxb.properties file in the exact same location as the class files
In my pom.xml also the underneath, to copy de jaxb.properties file to the exact same location. (check in you target folder if it worked, the properties file should be in the exact same folder.
org.apache.maven.plugins
maven-compiler-plugin
src/main/java
**/*.java
Check in your imports that for jakarta is used for the Unmarshaller etc.
So your import statements should be like this:
import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.Unmarshaller;
Good luck!
I am constructing a POC. And I created a pass through proxy service for Google Plus.
Without using any proxy service I get this is my output :
{
"kind":"plus#person",
"etag":"\"ExituU7aUpmkkfyD52VulzptThw/4J1clegrhxYC2fsJOu2XWCs1Ewg\"",
"id":"117488614303967062311",
"displayName":"Abhi NeoN",
"name":{
"familyName":"NeoN",
"givenName":"Abhi"
},
"tagline":"hey guys ! ssup!! check out ma recnt videos... uploaded",
"gender":"male",
"aboutMe":"\u003cb\u003ehie, abhishek - ma full name \u003c/b\u003e\u003cdiv\u003e\u003cb\u003em a DANCER ,\u003c/b\u003e\u003c/div\u003e\u003cdiv\u003e\u003cb\u003ei luv ma dancing .\u003c/b\u003e\u003c/div\u003e\u003cdiv\u003e\u003cb\u003ei care ma dancing ,\u003c/b\u003e\u003c/div\u003e\u003cdiv\u003e\u003cb\u003ei jus hv a gr8 thng in me dats ma dancing.\u003c/b\u003e\u003c/div\u003e",
"relationshipStatus":"single",
"url":"https://plus.google.com/117488614303967062311",
"image":{
"url":"https://lh6.googleusercontent.com/-tF-ip0tUxD4/AAAAAAAAAAI/AAAAAAAAAAA/WKI3USUh_DA/photo.jpg?sz=50"
},
"urls":[
{
"value":"https://plus.google.com/117488614303967062311",
"type":"profile"
},
{
"value":"https://www.googleapis.com/plus/v1/people/117488614303967062311",
"type":"json"
}
],
"organizations":[
{
"name":"our lady of nazareth high school",
"title":"science",
"type":"school"
},
{
"name":"",
"title":"BLUEBYTES",
"type":"work"
}
]
}
But when I try to do the same using a simple pass through service I get only :
{
"kind":"plus#person"
}
I read on the wso2esb site that they had a bug and the explanation given to resolve the bug was that json data received was not in the proper format.
But now how do I resolve the problem. I mean is their any way I can manipulate the json data before the esb converts it into json data.
We have solved this issue in the latest release of ESB (version 4.5.0). By default it comes with JSONMessageFormatter/JSONBuilder that can handle JSON payloads with multiple keys.
We also came up with another solution for handling message flows that involve different types of JSON <--> XML (or JSON <--> JSON) conversions. JSONStreamBuilder and JSONStreamFormatter can be used to implement such scenarios with the 'script' mediator. Have a look at sample #441 in ESB 4.5.0.
To run sample #441;
Add JSONStreamBuilder and JSONStreamFormatter as the builder and
formatter for JSON in repository/conf/axis2/axis2.xml file
Deploy SimpleStockQuoteService
Start the sample axis2server
Run the JSON client with 'ant newjsonclient'
This is one of the limitations of the current axis2 JSON builder/formatter. We are currently working on a new builder/formatter pair for JSON that does not convert JSON <-> XML. Instead it(builder) stores the JSON message as a stream and the script mediator can be used to build a JSON object out of that stream. For example, if we send {"a" : "x", "b" : "y"} as the request, within the ESB, we can manipulate this request as a JSON object with javascript.
var a = mc.getJSON().a.toString();
var b = mc.getJSON().b.toString();
mc.setPayloadXML(
<m:A xmlns:m="http://example.json">
<m:a>{a}</m:a>
<m:b>{b}</m:b>
</m:A>);
Similarly mc.setJSON() method can be used to set arbitrary JSON objects.
The only way to reliably convert json to xml and back again is via the use of type hints in the xml. the default converter does not do this. it
1. drops everything after the first property
2. confuses single element lists with properties when going from xml to json
i have reimplemented the transconversion classes using the json-util library, which converts the json to xml containing type hints as element attributes, to ensure no ambiguity.
in this way we can smart proxy (ie content route and mediate on transport and payload) for ALL json based rest services through WSO2 with no issues
This solves the problem (I think camel does it this way by default).
Here is the pom file and code:
place the jar into /repository/components/lib
you must update the messageformatter and messagebuilder mappings for content type "application/json" in axis2.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>wso2 json/xml converter</name>
<groupId>x.y.z</groupId>
<artifactId>wso2converter</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<jdk.version>1.6</jdk.version>
</properties>
<build>
<finalName>wso2converter</finalName>
<resources>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>${jdk.version}</source>
<target>${jdk.version}</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>1.0.1</version>
<executions>
<execution>
<id>enforce-jdk</id>
<phase>validate</phase>
<goals>
<goal>display-info</goal>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireJavaVersion>
<version>[${jdk.version},)</version>
</requireJavaVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.3</version>
<classifier>jdk15</classifier>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
<version>1.3.2</version>
<type>jar</type>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.apache.ws.commons.axiom</groupId>
<artifactId>axiom-api</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>org.apache.axis2</groupId>
<artifactId>axis2-kernel</artifactId>
<version>1.6.2</version>
</dependency>
<dependency>
<groupId>xom</groupId>
<artifactId>xom</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.apache.synapse</groupId>
<artifactId>synapse-core</artifactId>
<version>2.1.0</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-json</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.13</version>
<!--scope>provided</scope-->
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
package a.b.wso2;
import java.io.InputStream;
import net.sf.json.JSON;
import net.sf.json.JSONSerializer;
import net.sf.json.xml.XMLSerializer;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.util.AXIOMUtil;
import org.apache.axis2.AxisFault;
import org.apache.axis2.builder.Builder;
import org.apache.axis2.context.MessageContext;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
public class WsoJtoX implements Builder {
Logger logger = Logger.getLogger("a.b.wso2converter");
#Override
public OMElement processDocument(InputStream is, String contentType,
MessageContext messageContext) throws AxisFault {
String jsonData = "";
try {
jsonData = IOUtils.toString(is,"UTF-8");
String output = process(jsonData);
OMElement e = AXIOMUtil.stringToOM(output);
return e;
} catch (Exception e) {
logger.error("error converting json string " + jsonData, e);
if (e instanceof AxisFault) {
throw (AxisFault) e;
}
throw new AxisFault("(B"+counter+") error converting json to xml", e);
}
}
static int counter=0;
public String process(String jsonData) throws AxisFault {
try {
String tran = "__ns__";
jsonData=jsonData.replace("\r", "").trim();
//jsonData=jsonData.replace("\n", "");
String decoded = (jsonData.replaceAll("\"([a-zA-Z0-9_]*)\\:([a-zA-Z0-9]*)\"(\\s*)(:)", "\"$1" + tran + "$2\"$3:"));
counter++;
if (logger.isDebugEnabled()) {
logger.debug("\n>>>>> (B"+counter+") converting json\n " + jsonData + "\n====");
}
XMLSerializer serializer = new XMLSerializer();
JSON json = JSONSerializer.toJSON(decoded);
String xml = serializer.write(json);
//add in the soap stuff
StringBuilder sb = new StringBuilder();
sb.append("<soap:Envelope xmlns:soap=\"http://www.w3.org/2001/12/soap-envelope\" soap:encodingStyle=\"http://www.w3.org/2001/12/soap-encoding\"> <soap:Body>");
sb.append(xml.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", ""));
sb.append("</soap:Body></soap:Envelope>");
if (logger.isDebugEnabled()) {
logger.debug("\n==== (B"+counter+") to xml\n" + sb.toString()+"\n<<<<<");
}
return sb.toString();
} catch (Exception e) {
throw new AxisFault("(B"+counter+") error transforming json to xml", e);
}
}
}
package a.b.wso2;
import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import net.sf.json.JSON;
import net.sf.json.xml.XMLSerializer;
import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.OMOutputFormat;
import org.apache.axiom.om.util.AXIOMUtil;
import org.apache.axis2.AxisFault;
import org.apache.axis2.Constants;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.transport.MessageFormatter;
import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
public class WsoXtoJ implements MessageFormatter {
Logger logger = Logger.getLogger("a.b.wso2converter");
private static int counter=0;
public String convert(String xData) {
counter++;
if (logger.isDebugEnabled()) {
logger.debug("\n]]]]] (A"+counter+") converting xml\n " + xData + "\n-----");
}
try {
String tran = "__ns__";
XMLSerializer serializer = new XMLSerializer();
OMElement e = AXIOMUtil.stringToOM(xData);
OMElement b = (OMElement) e.getChildrenWithLocalName("Body").next();
b = (OMElement) b.getChildElements().next();
String xfrag = b.toStringWithConsume();
String str = "";
JSON j = serializer.read(xfrag);
str = j.toString();
String nstr = str.replaceAll("\"([a-zA-Z0-9_]+)" + tran + "([a-zA-Z0-9]+)\"(\\s*)(:)", "\"$1:$2\"$3:"); //", "\"$1:$2\"");
if (logger.isDebugEnabled()) {
logger.debug("\n----- (A"+counter+") to json\n" + nstr+"\n[[[[[");
}
return nstr;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override
public String formatSOAPAction(MessageContext msgCtxt, OMOutputFormat format,
String soapActionString) {
return null;
}
#Override
public byte[] getBytes(MessageContext ctx, OMOutputFormat format)
throws AxisFault {
String env="";
try {
OMElement element = ctx.getEnvelope().getBody().getFirstElement();
String payload = this.convert(element.toString());
return payload.getBytes(format.getCharSetEncoding());
} catch (UnsupportedEncodingException e) {
logger.error("(A"+counter+") error converting xml to json "+ctx.getEnvelope().toString());
throw AxisFault.makeFault(e);
}
}
#Override
public String getContentType(MessageContext msgCtxt, OMOutputFormat format,
String soapActionString) {
String contentType = (String) msgCtxt.getProperty(Constants.Configuration.CONTENT_TYPE);
String encoding = format.getCharSetEncoding();
if (contentType == null) {
contentType = (String) msgCtxt.getProperty(Constants.Configuration.MESSAGE_TYPE);
}
if (encoding != null) {
contentType += "; charset=" + encoding;
}
return contentType;
}
#Override
public URL getTargetAddress(MessageContext msgCtxt, OMOutputFormat format,
URL targetURL) throws AxisFault {
return targetURL;
}
#Override
public void writeTo(MessageContext msgCtxt, OMOutputFormat format,
OutputStream out, boolean preserve) throws AxisFault {
try {
out.write(this.getBytes(msgCtxt, format));
out.flush();
} catch (IOException e) {
throw AxisFault.makeFault(e);
}
}
}
I had the same problem.
In my experience, the JSON parser for WSO2 ESB (based on Axis2-json) supports only a subset of JSON:
The JSON has to start with "{", i.e. there can't be a JSONArray at the root.
Only the first key-value pair will be considered. This is because JSON is mapped to XML-like datastructure, and XML must have a root, so the first key-value pair is considered as root.
The value of the first key-value pair must not be an array. This because the converter has to know which XML tag should be used for each value:
e.g.: ... { "key": ["val1", "val2", ...]} -> <key>val1</key><key>val2</key>....
I have the same problem here and want to find a fix for this. My thoughts are to create a new JSONBuilder (the parser which builds the internal SOAP message construct) and the JSONFormatter (the serializer) to use a virtual root (e.g. { "root" : ... } ) to fake the parser.
I have web-service in .net. When I retrieve data from database, it returns JSON File in Android Mobile. How can I convert JSON File to XML Or text.
For a simple solution, I recommend Jackson, as it can transform arbitrarily complex JSON into XML with just a few simple lines of code.
import org.codehaus.jackson.map.ObjectMapper;
import com.fasterxml.jackson.xml.XmlMapper;
public class Foo
{
public String name;
public Bar bar;
public static void main(String[] args) throws Exception
{
// JSON input: {"name":"FOO","bar":{"id":42}}
String jsonInput = "{\"name\":\"FOO\",\"bar\":{\"id\":42}}";
ObjectMapper jsonMapper = new ObjectMapper();
Foo foo = jsonMapper.readValue(jsonInput, Foo.class);
XmlMapper xmlMapper = new XmlMapper();
System.out.println(xmlMapper.writeValueAsString(foo));
// <Foo xmlns=""><name>FOO</name><bar><id>42</id></bar></Foo>
}
}
class Bar
{
public int id;
}
This demo uses Jackson 1.7.7 (the newer 1.7.8 should also work), Jackson XML Databind 0.5.3 (not yet compatible with Jackson 1.8), and Stax2 3.1.1.
Here is an example of how you can do this, generating valid XML. I also use the Jackson library in a Maven project.
Maven setup:
<!-- https://mvnrepository.com/artifact/com.fasterxml/jackson-xml-databind -->
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>jackson-xml-databind</artifactId>
<version>0.6.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.6</version>
</dependency>
Here is some Java code that first converts a JSON string to an object and then converts the object with the XMLMapper to XML and also removes any wrong element names. The reason for replacing wrong characters in XML element names is the fact that you can use in JSON element names like $oid with characters not allowed in XML. The Jackson library does not account for that, so I ended up adding some code which removes illegal characters from element names and also the namespace declarations.
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.xml.XmlMapper;
import java.io.IOException;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Converts JSON to XML and makes sure the resulting XML
* does not have invalid element names.
*/
public class JsonToXMLConverter {
private static final Pattern XML_TAG =
Pattern.compile("(?m)(?s)(?i)(?<first><(/)?)(?<nonXml>.+?)(?<last>(/)?>)");
private static final Pattern REMOVE_ILLEGAL_CHARS =
Pattern.compile("(i?)([^\\s=\"'a-zA-Z0-9._-])|(xmlns=\"[^\"]*\")");
private ObjectMapper mapper = new ObjectMapper();
private XmlMapper xmlMapper = new XmlMapper();
String convertToXml(Object obj) throws IOException {
final String s = xmlMapper.writeValueAsString(obj);
return removeIllegalXmlChars(s);
}
private String removeIllegalXmlChars(String s) {
final Matcher matcher = XML_TAG.matcher(s);
StringBuffer sb = new StringBuffer();
while(matcher.find()) {
String elementName = REMOVE_ILLEGAL_CHARS.matcher(matcher.group("nonXml"))
.replaceAll("").trim();
matcher.appendReplacement(sb, "${first}" + elementName + "${last}");
}
matcher.appendTail(sb);
return sb.toString();
}
Map<String, Object> convertJson(String json) throws IOException {
return mapper.readValue(json, new TypeReference<Map<String, Object>>(){});
}
public String convertJsonToXml(String json) throws IOException {
return convertToXml(convertJson(json));
}
}
Here is a JUnit test for convertJsonToXml:
#Test
void convertJsonToXml() throws IOException, ParserConfigurationException, SAXException {
try(InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("json/customer_sample.json")) {
String json = new Scanner(in).useDelimiter("\\Z").next();
String xml = converter.convertJsonToXml(json);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
Node first = doc.getFirstChild();
assertNotNull(first);
assertTrue(first.getChildNodes().getLength() > 0);
}
}
No direct conversion API is available in android to convert JSON to XML. You need to parse JSON first then you will have to write logic for converting it to xml.
Standard org.json.XML class converts between JSON and XML in both directions. There is also tutorial on how to use it.
The conversion is not very nice as it does not create XML attributes at all (entities only), so XML output is more bulky than could possibly be. But it does not require to define Java classes matching the data structures that need to be converted.
Underscore-java library has static method U.jsonToXml(string). Live example
import com.github.underscore.U;
public class MyClass {
public static void main(String args[]) {
String json = "{\"Price\": {"
+ " \"LineItems\": {"
+ " \"LineItem\": {"
+ " \"UnitOfMeasure\": \"EACH\", \"Quantity\": 2, \"ItemID\": \"ItemID\""
+ " }"
+ " },"
+ " \"Currency\": \"USD\","
+ " \"EnterpriseCode\": \"EnterpriseCode\""
+ "}}";
System.out.println(U.jsonToXml(json));
}
}
Output:
<?xml version="1.0" encoding="UTF-8"?>
<Price>
<LineItems>
<LineItem>
<UnitOfMeasure>EACH</UnitOfMeasure>
<Quantity number="true">2</Quantity>
<ItemID>ItemID</ItemID>
</LineItem>
</LineItems>
<Currency>USD</Currency>
<EnterpriseCode>EnterpriseCode</EnterpriseCode>
</Price>