Parsing XML to get child nodes

apexxml

I am trying to parse the following XML Payload in Apex:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
    <soap:Header>
        <wsa:Action>UpdateResponse</wsa:Action>
        <wsa:MessageID>urn:uuid:62bffb2d-feb3-44ba-9dea-c43852979252</wsa:MessageID>
        <wsa:RelatesTo>urn:uuid:0baed942-04e1-4b0c-a2de-1085e15ca4d4</wsa:RelatesTo>
        <wsa:To>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
        <wsse:Security>
            <wsu:Timestamp wsu:Id="Timestamp-a1ed0755-8da2-4dce-9eb3-5fb4898faf6b">
                <wsu:Created>2021-08-19T14:42:18Z</wsu:Created>
                <wsu:Expires>2021-08-19T14:47:18Z</wsu:Expires>
            </wsu:Timestamp>
        </wsse:Security>
    </soap:Header>
    <soap:Body>
        <UpdateResponse
            xmlns="http://exacttarget.com/wsdl/partnerAPI">
            <Results>
                <StatusCode>OK</StatusCode>
                <StatusMessage>Updated Subscriber.</StatusMessage>
                <OrdinalID>0</OrdinalID>
                <Object xsi:type="Subscriber">
                    <PartnerKey xsi:nil="true" />
                    <ID>171465903</ID>
                    <ObjectID xsi:nil="true" />
                    <SubscriberKey>[email protected]</SubscriberKey>
                    <Status>Unsubscribed</Status>
                </Object>
            </Results>
            <Results>
                <StatusCode>Error</StatusCode>
                <StatusMessage>InvalidEmailAddress</StatusMessage>
                <OrdinalID>1</OrdinalID>
                <ErrorCode>12000</ErrorCode>
                <Object xsi:type="Subscriber">
                    <PartnerKey xsi:nil="true" />
                    <ObjectID xsi:nil="true" />
                    <SubscriberKey>[email protected]</SubscriberKey>
                    <Status>Active</Status>
                </Object>
            </Results>
            <RequestID>86ba4919-99da-4593-b2e3-622a19b9f361</RequestID>
            <OverallStatus>Has Errors</OverallStatus>
        </UpdateResponse>
    </soap:Body>
</soap:Envelope>

I need to iterate over the Results child items and retrieve the StatusCode, StatusMessage and child SubscriberKey.

I have attemped to this using the following code but I can't get the first attribute:

DOM.Document document = new DOM.Document();
document.load(responseBody);

for (DOM.XmlNode node : document.getRootElement().getChildren())
{
    
    DOM.XmlNode statusCode = node.getChildElement('UpdateResponse', 'http://exacttarget.com/wsdl/partnerAPI');
    String statusMsg = node.getChildElement('Results', null).getAttribute('StatusCode', null);

}

Best Answer

Nested data structures like this generally require nested loops to extract data.

for(DOM.XmlNode node : document.getRootElement().getChildren()){ only gets you the direct children of the root element. That is, the <soap:Header> and <soap:Body>

To get at your target data, you need to make some additional calls to getChildren().

Also, an "attribute" in XML is something contained inside of a tag in double quotes. E.g. <myXmlTag IAmAnAttribute="And I'm the attribute's value">. The StatusCode, StatusMessage, and SubscriberKey are all Text nodes of their respective tags

What you're looking for here is something closer to this

String statusCode;

for (DOM.XmlNode node : document.getRootElement().getChildren()){
    if(node.getName() == 'Body'){
        for (DOM.XmlNode node2 : node.getChildren()){
            if(node.getName() == 'UpdateResponse'){
                for (DOM.XmlNode node3 : node2.getChildren()){
                    if(node3.getName() == 'Results'){
                        for (DOM.XmlNode node4 : node3.getChildren()){
                            if(node4.getName() == 'StatusCode'){
                                statusCode = node4.getText();
                            }
                        }
                    }
                }
            }
        }
    }
}