0

I have an external system I am making a SOAP HTTP POST request to that has a simple response. I need to store both returned values as string variables for further processing. Here is the response from the server I am trying to parse:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <AddDataResponse xmlns="https://generic-api.website.com">
      <AddDataResult>
        <RecordID>1234</RecordID>
        <ErrorDescription>something went wrong</ErrorDescription>
      </AddDataResult>
    </AddDataResponse>
  </soap:Body>
</soap:Envelope>

I have tried several patterns, but end up with the values being null. Here are the patterns I have tried:

//create DOM doc with HTTPResponse body

Dom.Document doc = res.getBodyDocument();
Dom.XMLNode root = doc.getRootElement();

string RecordId = root.getChildElement('RecordID', null).getText().trim();
string Error = getChildElement('ErrorDescription', null).getText().trim();

Here is a separate class pattern I tried , but the text values return null as well.

 public class XMLparser{

  Public void parseXML(String xmlfile)
  {
       DOM.Document xmlDOC = new DOM.Document();
       xmlDOC.load(xmlfile);
       DOM.XMLNode rootElement = xmlDOC.getRootElement();
       System.Debug('$$$$ Root Element'+rootelement);

       for(DOM.XMLNode xmlNodeObj:xmlDOC.getRootElement().getChildElements()){

       System.Debug('$$$ Child Elements'+xmlNodeObj);
       for(DOM.XMLNode xmlNodeObjChild:xmlNodeObj.getChildren())
       {
       System.Debug('$$$ Childrens'+xmlNodeObjChild.getChildren());      
             if(xmlNodeObjChild.getName()=='RecordId')
             System.Debug(xmlNodeObjChild.getText().trim());
             if(xmlNodeObjChild.getName()=='ErrorDescription')
             System.Debug(xmlNodeObjChild.getText().trim());

                } 
            }
        }
    }

Here is the debug log for the execution. It remains consistent. There seems to be 5 null values that come before the text value I am after.

    [XMLNode[ELEMENT,AddDataResult,https://generic-api.website.com,null,null,
    [XMLNode[ELEMENT,RecordID,https://generic-api.website.com,null,null,
    [XMLNode[TEXT,null,null,null,null,null,1234,]],null,],
    [XMLNode[ELEMENT,ErrorDescription,https://generic-api.website.com,null,null,
    [XMLNode[TEXT,null,null,null,null,null,Client Inactive,]],null,]],null,]],null,]],null,]],null,]"|0xbcd6e9b

The values I am after are present in the text nodes, but I can't seem to get the value at the end of the delimited null values. Is there a better way to parse the response or to move past the null values? Any input is appreciate. Thanks!

3

I hate XML parsing.

here you go though. this will get you the Record ID after traversing down the tree

HTTPresponse resp = New HttpResponse();
resp.setBody('<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <AddDataResponse xmlns="https://generic-api.website.com"> <AddDataResult> <RecordID>1234</RecordID> <ErrorDescription>something went wrong</ErrorDescription> </AddDataResult> </AddDataResponse> </soap:Body> </soap:Envelope>');

dom.document doc = resp.getBodyDocument();
Dom.XMLNode root = doc.getRootElement();
DOM.XmlNode bodyNode = root.getChildElement('Body', 'http://schemas.xmlsoap.org/soap/envelope/');
DOM.XmlNode adresponseNode = bodyNode.getChildElement('AddDataResponse','https://generic-api.website.com');
DOM.XmlNode adresultNode = adresponseNode.getChildElement('AddDataResult','https://generic-api.website.com');
String recordId = adresultNode.getChildElement('RecordID','https://generic-api.website.com').getText();
System.debug(recordId);

You can remove the response part as it is there for testing. You can however copy and paste the example in exec anon to see it works. I had to get every child element

This is what I used to get to the value needed for each. When I used null for namespace I got back null so NS was required - below code is not written to be best practice so don't critique lol

for (DOM.XmlNode x : root.getChildren()) {
    System.debug(x.getName());
    System.debug(x.getNamespace());
    for (DOM.XmlNode child : x.getChildren()) {
        System.debug(child.getName());
        System.debug(child.getNamespace());
        for (DOM.XmlNode child2 : child.getChildren()) {
            System.debug(child2.getName());
            System.debug(child2.getNamespace());
            for (DOM.XmlNode child3 : child2.getChildren()) {
                System.debug(child3.getName());
                System.debug(child3.getNamespace());

            }


        }

    }

}
  • Should all of the namespaces be defined? Shouldn't some be null? – Adrian Larson Mar 9 '17 at 2:11
  • @AdrianLarson - When I left them null I got back null (Using the OP example XML Doc) so......I will post what I used to see what the value were – Eric Mar 9 '17 at 2:12
  • @JoshuaWS - Did it solve it enough to mark the answer as accepted so other can find it when searching for similar issues? – Eric Mar 23 '17 at 0:27
0

You can use my XMLSerializer to parse the data. Here is the link: https://github.com/skolakan/Apex-XML-Serializer

Usage: First define class structure to match your soap response XML

public class SoapResponse {    

    public EnvelopeModel Envelope {get;set;}

    public class EnvelopeModel{
        public BodyModel Body{get;set;}
    }

    public class BodyModel{
        public AddDataResponseModel AddDataResponse{get;set;}
    }
    public class AddDataResponseModel{
        Public AddDataResultModel AddDataResult{get;set;}
    }

    public class AddDataResultModel{
        public string RecordId {get;set;}
        public string ErrorDescription {get;set;}
    }
}

Then deSerialize soap response to apex class

//sample response
String xmlString = '<?xml version="1.0" encoding="UTF-8"?><soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><soap:Body><AddDataResponse xmlns="https://generic-api.website.com"><AddDataResult><RecordID>1234</RecordID><ErrorDescription>something went wrong</ErrorDescription></AddDataResult></AddDataResponse></soap:Body></soap:Envelope>';
//Modify tags with soap: prefix because the properties cannot have :
xmlString = xmlString.replace('soap:','');
//deserialize
SoapResponse response = (SoapResponse)XMLSerializer.deserialize(xmlString,SoapResponse.class);

Now you can access the data from apex object

System.debug('RecordId:' + response.Envelope.Body.AddDataResponse.AddDataResult.RecordId);
System.debug('ErrorDescription:' + response.Envelope.Body.AddDataResponse.AddDataResult.ErrorDescription);

This will output

09:49:13:012 USER_DEBUG [5]|DEBUG|RecordId:1234
09:49:13:012 USER_DEBUG [6]|DEBUG|ErrorDescription:something went wrong
  • I am trying this approach with a slightly different process and will provide an update shortly! – JoshuaWS Mar 23 '17 at 17:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.