Json To xml transformation using jackson , XPath and Mvel script.
This tutorial mainly focuses on the transformation of Json payload to xml payload in generic configurable way using Jackson, XPath and Mvel script.
Design:
The intention of this design to keep the transformation logic away from the application code with the help of Mvel script. This Mavel script contains the actual transformation logic of extraction of source value from Json using Jackson library and substitute this value in the given xml template using XPath.
We can have this Mvel script stored somewhere in db or in any configuration resource folder and can pass dynamically to our application transformation logic.
- JsonUtils : This Utils parse the input Json and provides the method to read/extract the node value based on the given path.
- XPathUtils: This Utils helps in parsing the Xml document and read or set the value in the given xml document.
- Mvel Engine: This component executes the given Mvel Script, which internally uses the JsonUtils and XPathUtils to execute the expressions for transformation.
<emp>
<empId></empId>
<empName></empName>
</emp>
{
employee:
{
"id":"E1201",
"name":"Guvvalas"
}
}
Mvel Script:
def jsonToXml(){
Object jNode= jpath.evaluate('/employee/id',source);
String idVal=jNode.asText();
Object node=xpath.evaluateNode('/emp/empId',target);
node.setTextContent(idVal);
Object jNameNode= jpath.evaluate('/employee/name',source);
String nameVal=jNameNode.asText();
Object empNameNode=xpath.evaluateNode('/emp/empName',target);
empNameNode.setTextContent(nameVal);
}
jsonToXml();
Output Xml:
<emp>
<empId>E1201</empId>
<empName>Guvvalas</empName>
</emp>
We know that using Mvel script we can invoke/execute the java code. The above script actually contains the java code and calls the JsonUtils and XPathUtils to apply the transformation logic of extraction and inserting the values in the template.
Steps For the Implementation:
- Create a spring boot project and add the following dependencies in the pox.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.2</version>
</dependency>
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.4.15.Final</version>
</dependency>
- Implement JQueryUtils.java as below. This class used the Jackon library to parse the given Json node and extract the value for the given path.
public class JQueryUtils {
/**
*
* @param path
* @param root
* @return
*/
public JsonNode evaluate(String path, JsonNode root){
return root.at(path);
}
}- Implement the XPathUtils.java as below.
- This class uses the XPath library to read and set the value for the given xml document.
public class XPathUtils {
private static XPath xPath = XPathFactory.newInstance().newXPath();
public static String evaluate(String expression, Document xmlDocument) throws Exception {
return xPath.evaluate(expression, xmlDocument);
}
/**
*
* @param expression
* @param xmlDocument
* @return
* @throws Exception
*/
public static Element evaluateNode(String expression, Document xmlDocument ) throws Exception {
return (Element)xPath.evaluate(expression, xmlDocument, XPathConstants.NODE);
}
/**
*
* @param doc
* @return
* @throws Exception
*/
public static String docAsString(Document doc) {
try{
StringWriter writer = new StringWriter();
Transformer tr = TransformerFactory.newInstance().newTransformer();
tr.setOutputProperty( OutputKeys.INDENT, "yes" );
tr.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
tr.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
tr.transform(new DOMSource(doc), new StreamResult(writer));
return writer.getBuffer().toString();
}catch(Exception e){
throw new RuntimeException(e);
}
}
}- Implement the MvelUtils.java . This class is actually Mvel parser which takes the script and variable resolver map , which replaces the variables with actual references which executing the script.
public class MvelUtils {
public static Object evaluate(String mvelScript,Map<String,Object> params){
MapVariableResolverFactory vars = new MapVariableResolverFactory(new HashMap(params));
return MVEL.eval(mvelScript, vars);
}
}- AppConstants.Java
public class AppConstants {
public static final String XPATH="xpath";
public static final String JPATH="jpath";
public static final String SOURCE_ENTITY="source";
public static final String TARGET_ENTITY="target";
}
- Finally implement the JsonToXmlParser.java service class . This class take the json, xml template and script as input and does the transformation by using all the above classes.
- @Slf4j
@Service
public class JsonToXmlParser {
private final ObjectMapper mapper = new ObjectMapper();
private final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
/**
*
* @param json
* @param xmlTemplate
* @param mvelScript
* @param <T1>
* @param <T2>
* @return
* @throws Exception
*/
public<T1,T2> T2 transform(String json,String xmlTemplate,String mvelScript) throws Exception {
//convert json string to JsonNode
var jsonNode = mapper.readTree(json);
//Convert Xml String to Xml Document
DocumentBuilder builder = factory.newDocumentBuilder();
Document targetDoc = builder.parse(new InputSource(new StringReader(xmlTemplate)));
//Create the MVel script variable with reference
Map<String, Object> vars = new HashMap<String, Object>();
vars.put(AppConstants.SOURCE_ENTITY, jsonNode);
vars.put(AppConstants.TARGET_ENTITY, targetDoc);
vars.put(AppConstants.JPATH, new JQueryUtils());
vars.put(AppConstants.XPATH, new XPathUtils());
//execute the MVel scripts
var val = MvelUtils.evaluate(mvelScript, vars);
return (T2) XPathUtils.docAsString(targetDoc);
}
}
- Testcase to test the above transformation logic.
@Test
public void jsonToXmlParser() throws Exception {
var json = "{\"employee\":{\"id\":\"123\",\"name\":\"testName\"}}";
var template = "<emp><empId></empId><empName></empName></emp>";
var script = "def jsonToXml(){" +
"Object jNode= jpath.evaluate('/employee/id',source);" +
"String idVal=jNode.asText();" +
"Object node=xpath.evaluateNode('/emp/empId',target);" +
"node.setTextContent(idVal);" +
"Object jNameNode= jpath.evaluate('/employee/name',source);" +
"String nameVal=jNameNode.asText();" +
"Object empNameNode=xpath.evaluateNode('/emp/empName',target);" +
"empNameNode.setTextContent(nameVal);" +
"} " +
"jsonToXml();";
var output = jsonToXmlParser.transform(json, template, script);
System.out.println(output);
}- output will be:
<emp>
<empId>E1201</empId>
<empName>Guvvalas</empName>
</emp>
View the above code @Git Repo.
This tutorial is very useful, easy to integrate, and implement. The concept is explained very nicely.
ReplyDelete