
When you are working on an integration which uses XML instead of JSON then it’s always a pain to parse the XML response in apex. I encountered the same problem and ended up with writing a class which makes this job easier. I think this can help you as well.
XMLParser class has a method xmlToJson which takes a XML string and returns a JSON string.
Example – Following is the xml
<?xml version="1.0" encoding="UTF-8"?> <breakfast_menu> <food> <name>Belgian Waffles</name> <price>$5.95</price> <description>Two of our famous Belgian Waffles with plenty of real maple syrup</description> <calories>650</calories> </food> <food> <name>Strawberry Belgian Waffles</name> <price>$7.95</price> <description>Light Belgian waffles covered with strawberries and whipped cream</description> <calories>900</calories> </food> <food> <name>Berry-Berry Belgian Waffles</name> <price>$8.95</price> <description>Light Belgian waffles covered with an assortment of fresh berries and whipped cream</description> <calories>900</calories> </food> <food> <name>French Toast</name> <price>$4.50</price> <description>Thick slices made from our homemade sourdough bread</description> <calories>600</calories> </food> <food> <name>Homestyle Breakfast</name> <price>$6.95</price> <description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description> <calories>950</calories> </food> </breakfast_menu>
JSON returned by the XMLParser
{ "breakfast_menu": { "food": [ { "name": "Belgian Waffles", "price": "$5.95", "description": "Two of our famous Belgian Waffles with plenty of real maple syrup", "calories": "650" }, { "name": "Strawberry Belgian Waffles", "price": "$7.95", "description": "Light Belgian waffles covered with strawberries and whipped cream", "calories": "900" }, { "name": "Berry-Berry Belgian Waffles", "price": "$8.95", "description": "Light Belgian waffles covered with an assortment of fresh berries and whipped cream", "calories": "900" }, { "name": "French Toast", "price": "$4.50", "description": "Thick slices made from our homemade sourdough bread", "calories": "600" }, { "name": "Homestyle Breakfast", "price": "$6.95", "description": "Two eggs, bacon or sausage, toast, and our ever-popular hash browns", "calories": "950" } ] } }
Syntax
String xml = ''; // XML String jsonContent = XmlParser.xmlToJson(xml); // You can deserailize the xml like we do JSON in apex using JSON.deserilize method ResponseWrapper rw = (ResponseWrapper) JSON.deserilaize(jsonContent, ResponseWrapper.class);
XMLParser class
/* An utility class to parse XML and create the equivalent JSON @author - Naval Sharma */ public class XMLParser { // To find the root element so that we can enclose it in the curly braces public static String rootElementName; /* Method which is parsing the XML content into JSON * @param xml : XML String * return : JSON String */ public static String xmlToJson(String xml) { // Load the xml in the document Dom.Document doc = new Dom.Document(); doc.load(xml); Dom.XMLNode root = doc.getRootElement(); // Pass the root element and false as the second parameter String jsonContent = XMLParser.parse(root, false); return jsonContent; } /* Method which makes the recursive calls and creates the JSON for * each element, it processes each node and finds the attributes and text content of a node * @param node : Dom.XMLNode instance - XML node which will be processed * @param isChild : Boolean - To control the structure of JSON, should be true for child element * return : JSON string */ public static String parse(Dom.XMLNode node, Boolean isChild){ String json = ''; Boolean isArray = false; if ( rootElementName == null ) { rootElementName = node.getName(); } if ( node.getNodeType() == Dom.XmlNodeType.ELEMENT ){ Map<String, List<String>> mapChildrenJSON = new Map<String, List<String>>(); List<String> lstJSONForChildren = new List<String>(); // Check whether node has any child List<Dom.XMLNode> children = node.getChildElements(); if ( children.size() > 0 ){ // Process all the children in a row for ( Dom.XMLNode child : children ){ String tmp = parse( child, true ); if( tmp != '' ) { if ( !mapChildrenJSON.containsKey( child.getName() ) ){ mapChildrenJSON.put( child.getName(), new List<String>() ); } // Add into a map to make a collection for the repeatative child nodes mapChildrenJSON.get( child.getName() ).add( tmp ); } } // Strcuture the JSON based on the repeation // Should be treated as an array if there are multiple elements with the same node name for ( String key : mapChildrenJSON.keySet() ){ if ( mapChildrenJSON.get(key).size() > 1 ){ if(isChild) { lstJSONForChildren.add( '[' + String.join(mapChildrenJSON.get(key), ', ') + ']' ); } else { lstJSONForChildren.add( '"' + key + '": [' + String.join(mapChildrenJSON.get(key), ', ') + ']' ); } isArray = true; } else { lstJSONForChildren.add( '"' + key + '": ' + mapChildrenJSON.get(key)[0] ); } } } // Construc the JSON for all the node attributes List<String> lstAttributes = new List<String>( lstJSONForChildren ); for ( Integer i=0; i<node.getAttributeCount(); i++){ String key = node.getAttributeKeyAt( i ); String value = node.getAttribute( key, '' ); lstAttributes.add( '"' + key + '": "' + value + '"' ); } // Look for the text content String textContent = node.getText(); if ( textContent != null && textContent.trim() != '' ) { textContent = textContent.replace( '"', '\\"' ); lstAttributes.add( '"ele_text": "' + textContent + '"' ); } if ( !isChild ){ if(!isArray) { json = '"' + node.getName() + '": {' + String.join(lstAttributes, ', ') + '}'; } else { json = ' {' + String.join(lstAttributes, ', ') + '}'; } } else { if ( lstAttributes.size() == 1 && textContent != null && textContent.trim() != '' ){ json = '"' + textContent + '"'; } else { if(!isArray) { if( lstAttributes.size() > 0 ){ json = '{' + String.join(lstAttributes, ', ') + '}'; } } else { json = String.join(lstAttributes, ', '); } } } } if ( rootElementName == node.getName() ) { if(!isArray) { json = '{' + json + '}'; } else { json = '{"' + node.getName() + '" : ' + json + '}'; } } system.debug(node.getName()+ ':' + json); return json; } }
Link to GitHub Repository
Deserializing xml into any wrapper class object
String jsonStr = XMLParser.xmlToJson(xmlStr);
WrapperClass wc = (WrapperClass) JSON.class(jsonStr, WrapperClass.class);
By Naveen Sharma
October 8, 2019Really great article and sample code. I think I did find a bug when there is a hierarchy involved in the XML. So for example, I get the following XML back from a web service and the code does not return valid JSON:
0000000024806ADAP-001ADAPTAVIST INC0001900-01-011THE HOLLAND BUILDING205 PARK CENTRAL EASTSPRINGFIELDMO658060001900-01-012ADAPTAVIST UK SERVICES LTUNIT 2 WATERSIDELONDONN1 7UX0001900-01-01BACS0001900-01-01000001