In a recent project we had to read XML data sent from an external  system and update the records in Salesforce. The external system had a REST API that accepted ExternalID as an input and gave us updated values based on the ExternaID. We then used these updated values to update the records in the system. In the process of building our project we discovered that there was very little information on reading XML inside an Apex class.

When integrating with an external system we have to use global standards or a universally recognizable language/ protocol that is recognizable by both systems in integration. Force.com supports global standards of SOAP and REST based API. You can read more about the two on the help topic on API.  For data transfer between the two systems we have different options like XML, JSON and even plain text. While using the REST API we can send values via XML, JSON or even plain text in its body. SOAP supports only XML, however, XML is the most popular way of data transfer between systems.

XML is a freeform markup language that allows you to define elements using a schema, and is typically used to communicate between machines in a human readable format. The beauty is that XML can be used to transmit data between any two systems, whether they are cloud systems or on-premises servers.  We could get the status of cake being baked in a microwave into the Force.com database in the future or we could connect to any other system like home-away, work.com, oracle or any other system that knows how to read XML and send XML. When I first learned that XML is the language that is human and machine readable I was very very excited, and I thought this was my ticket to program my own terminator. Sadly after 48 hours I learned that project terminator was still a distant dream. Another thing I learned was that reading an XML from Apex is very easy if you know what you are doing.

The following is an example of a XML that we will be receiving from the external server, we are going to read it and insert its value in Account Object.

<Accounts>
    <Account>
        <Type>Prospect</type>
        <Industry>Manufacturing</industry>
        <Employees>680</employees>
    </Account>
    <Account Name="Global media">
        <Type>Prospect</type>
        <Industry>Media</industry>
        <Employees>14688</employees>
    </Account>
</Accounts>

To get the data from the external system, we must first send a request to the server mentioning what we exactly need. Let us assume that the external system primary key is stored as AccountExternalID__c custom field in Account data.

We will be using the following steps to access data.

  1. We will first send an XML containing the AccountExternalID__c and send them to the URL of external system.
  2. We will get another XML in response from the external system that will be parsed in APEX. We will convert this XML into SObject and update the values.

Let us dig into the code

//SendRequest function sends a set of XML data to an END point URL. The RAW data is then processed in ReadResponse that will convert the XML into a List
public void sendRequest() {
    // Get the list of accounts for which we need the updated values from the external system
    List < Account > accountList = new List < Account([Select AccountExternalID__c 
                                                                from Account 
                                                                limit 2]);

    // Start preparing the XML
    XmlStreamWriter w = new XmlStreamWriter();
    w.writeStartDocument(null, '1.0'); //Start the XML document
    w.writeStartElement(null, 'accounts', null); //this will start with <accounts> in XML

    //Loop through the accounts we queried earlier and put the values into XML
    for (Account a: accountList) {
        // This will write <account> in the XML file
        w.writeStartElement(null, 'account', null);
        // Open the tag <accountID> that will be identified by the external system
        w.writeStartElement(null, 'accountid', null); 
        // Write the value of externalID to be send
        w.writeCharacters(a.AccountExternalID__c); 
        //close </accountid>
        w.writeEndElement(); 
        //close </account>
        w.writeEndElement(); 
    }

    //close </accounts>
    w.writeEndElement(); 
    w.writeEndDocument();

    String xmlOutput = w.getXmlString();

    w.close();

    //Now that we have XML we will pass this to external system using HttpRequest
    System.HttpRequest request = new System.HttpRequest();

    // Set the endpoint URL previously decided that will know how to handle the XML 
    // we send them. Basically it will contain the code to read the values we are 
    // sending and send the data back.
    request.setEndpoint('https://integration.yourService.com/someEnpoint'); 
    // This line is important to tell the server you are passing XML input.
    request.setHeader('Content-Type', 'application/xml'); 
    request.setMethod('POST');
    // Set the XMLOutput we have created in the class above
    request.setBody(xmlOutput); 

    //This is like dialing on a telephone, we send the data and wait for the response.
    System.HttpResponse response = new System.Http().send(request); 
    this.Response = response.getBody();
    //And here we read the response. We will then process the response
    XmlStreamReader reader = new XmlStreamReader(this.Response); 
    readResponse(reader);
}

Reading from XML file is similar to reading any file using computer code. We use the following steps for reading any file,

  1. Find the opening word in the file.
  2. Find the tokens to identify.
  3. Repeat step 2 till you reach the end.

The remote server is programmed to read the XML we have send and return us the Accounts we requested in the following format.

<?xml version="1.0" encoding="UTF-8" ?>
<Accounts>
    <Account>
        <Type>Prospect</type>
        <Industry>Manufacturing</industry>
        <Employees>680</employees>
    </Account>
    <Account Name="Global media">
        <Type>Prospect</type>
        <Industry>Media</industry>
        <Employees>14688</employees>
    </Account>
</Accounts>

In our code we are going to read the file line by line and collect to store it into a List. The flowchart of what we will be doing is as follows:

Now, let us write the code to read the file based on the flowchart above.

public void readResponse(XmlStreamReader reader) {
    // Account record we will be looping
    Account accountRecord; 
    // List of accounts to store the value
    List < Account > accountList = new list < Account > (); 
    // Is there any element next?
    while (reader.hasNext()) {  
        // Is the next element an opening tag?
        if (reader.getEventType() == XmlTag.START_ELEMENT) { 
            // Check if the first element is Account
            if ('Account' == reader.getLocalName()) {
                // if opening tag of account is found initialize AccountRecord
                accountRecord = new Account();
            } else if ('Type' == reader.getLocalName()) {
                // If you find any other opening tag, extract the string value
                accountRecord.type = getValueFromTag(reader);
            } else if ('Industry' == reader.getLocalName()) {
                // If you find any other opening tag, extract the string value
                accountRecord.industry = getValueFromTag(reader);
            } else if ('Employees' == reader.getLocalName()) {
                // If you find any other opening tag, extract the string value
                accountRecord.employees = getValueFromTag(reader);
            }
        } else if (reader.getEventType() == XmlTag.END_ELEMENT) {
            // Is the next element an end tag? If yes is it an Account or an Accounts tag?
            if ('Account' == reader.getLocalName()) {
                // If you find end tag called account, push the account record in list
                accountList.add(accountRecord);
            } else if ('Accounts' == reader.getLocalName()) {
                // We have reached end of file, just exit
                break;
            }
        }
    }
}

// This is an extra function to read data between opening and closing tag. 
// It will return the string of value from between tags
public string getValueFromTag(XMLStreamReader reader) {
    String DataValue;

    while (reader.hasNext()) {
        if (reader.getEventType() == XmlTag.END_ELEMENT) {
            break;
        } else if (reader.getEventType() == XmlTag.CHARACTERS) {
            DataValue = reader.getText();
        }
        reader.next();
    }

    return DataValue;
}

The above code will read the XML and store the values in AccountList. You can decided what happens to the AccountList at the end of the function, if we want the AccountList to be processed further or if the accounts are going to be updated.

In Conclusion

Reading XML input from a remote server is much easier than you might think it. It opens a wide range of possibilities for functionality that can be achieved by integrating Force.com platform with any system in the world. Learning XML has its own advantage: First of all it is an open standard, hence most systems will support it. It is a cool language, its very straightforward, and when the machines do rise, we will have the language our enemy speaks, which  will be handy for intercepting all their messages.

May the Force be with you.

tagged , , Bookmark the permalink. Trackbacks are closed, but you can post a comment.
  • Udi Shvekey

    Thanks! very good and easy to follow!

  • thatherahere

    Just worked on the similar thing few days before.
    Thanks for Sharing. Will really helpful for others.
    +1

  • Bhawani Sharma

    That’s great. But is there any specific reason, you didn’t use DOM classes?
    The reason I am asking is, I am big fan of DOM and if there is any drawbacks in using DOM, I will go with XMLStream classes.

    • Bryan Anderson

      I agree. I think the DOM classes are easier in my opinion but if there is a good reason to use the reader and writer classes I’m all for it.

    • thatherahere

      Hi,
      Parse an XML with DOM and XMLStreamReader and than Just look at number of statements executed in both. Also the DOM classes are not able to get Data placed inside CDATA tag. In CDATA case only XMLStreamReader can help you.
      I found these things just few days before. Before this me too was a big fan of DOM.xmlNode.

      [Right me if I'm going wrong]

      • Bhawani Sharma

        In case of DOM, you can directly access the node without any looping. Like:
        RootNode.getChildElement(‘elementName’, namespace). So definitely script statement will be lesser in DOM.
        Regarding CDATA, in above example, was there anything related to CDATA?

        I am only trying to understand, why stream classes were used above.

  • Thomas Gagne
  • Manuel

    Hello,

    Could you please point me out where do you increment the pointer in the while loop (line 7, function readResponse). Thanks.