Parse XML with namespace by SimpleXML in PHP


The XML send by outbound message from Salesforce comes with namespace which I cannot parse as most XML tags by SimpleXML.

When google for related solution, it seems children might be the solution, but I cannot find an example that match my case. There are other potential solution like xpath, SoapServer, XML Parser, XMLReader, and even Force.com Toolkit for PHP. But I preferred to keep it simple, and focus on SimpleXML.

After test different combination several times, I finally make it work for my case. Here is how:

Basic rules of children() and namesapce

For XML tags, if

  1. the name comes with namespace, use children(‘namespace’) before the tag.
  2. the name doesn’t comes with namespace, use the tag name.
  3. the tag contains a xmlns, use children(‘namespace’) before the tag.
  4. the tag contains a definition of a namespace by xmlns, the namespace might be references in children later.

It’s difficult to understand above rules. I will show you an example to help.

XML with namespace from Salesforce Outbound Message

Here is the sample xml file we need to use. It is generated by Salesforce Outbound Message and has many namespace in these tags.

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <soapenv:Body>
  <notifications xmlns="http://soap.sforce.com/2005/09/outbound">
   <OrganizationId>00D80000000K6bBEAS</OrganizationId>
   <ActionId>04k80000000L9RIAA0</ActionId>
   <SessionId xsi:nil="true"/>
   <EnterpriseUrl>https://na6.salesforce.com/services/Soap/c/29.0/00D80000000K6bB</EnterpriseUrl>
   <PartnerUrl>https://na6.salesforce.com/services/Soap/u/29.0/00D80000000K6bB</PartnerUrl>
   <Notification>
    <Id>04l8000000k9IvgAAE</Id>
    <sObject xsi:type="sf:Lead" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
    <sf:Id>00Q80000018pGmFEAU</sf:Id>
    <sf:CreatedDate>2013-11-16T03:53:20.000Z</sf:CreatedDate>
    <sf:LastName>Amigo</sf:LastName>
    </sObject>
   </Notification>
  </notifications>
 </soapenv:Body>
</soapenv:Envelope>

Parse XML with namespace for tag value

I get the filed value of CreatedDate by following code fragment.
$rcXML-&gt;children('http://schemas.xmlsoap.org/soap/envelope/')-&gt;Body-&gt;children('http://soap.sforce.com/2005/09/outbound')-&gt;notifications-&gt;Notification-&gt;sObject-&gt;children('urn:sobject.enterprise.soap.sforce.com')-&gt;CreatedDate.PHP_EOL;

$rcXML is the object of the interpreted XML file by simplexml_load_string(). What I need to access is in between of <sf:CreatedDate> and </sf:CreatedDate>.

First, I need to drill down from begin. $rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/&#8217;)->Body point to the block between <soapenv:Body> and </soapenv:Body>. The definition of soapenv is “http://schemas.xmlsoap.org/soap/envelope/&#8221; which may be found in the second line of the xml file as xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/&#8221;. If you use “soapenv” in children() as mentioned in the post in StackOverflow, it won’t work. You should follow the syntax in this article by Kevin Yank.

Next, $rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/&#8217;)->Body->children(‘http://soap.sforce.com/2005/09/outbound&#8217;)->notifications point to the block between <notifications xmlns=”http://soap.sforce.com/2005/09/outbound”&gt; and </notifications>. The xmlns=”http://soap.sforce.com/2005/09/outbound&#8221; means it is the default namespace for notifications. That’s why we have to use children().

$rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/&#8217;)->Body->children(‘http://soap.sforce.com/2005/09/outbound&#8217;)->notifications->Notification point to the block between <Notification> and </Notification>. You may get the value of <Id> by $rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/&#8217;)->Body->children(‘http://soap.sforce.com/2005/09/outbound&#8217;)->notifications->Notification->Id

$rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/&#8217;)->Body->children(‘http://soap.sforce.com/2005/09/outbound&#8217;)->notifications->Notification->sObject point to the block between <sObject xsi:type=”sf:Lead” xmlns:sf=”urn:sobject.enterprise.soap.sforce.com”> and </sObject>. It contains namespace definition for sf and is not the default namespace, we don’t need to use children() before sObject. BTW, urn is Universal Resource Name, Some names space use it instead of url which means Uniform Resource Locator.

Last, $rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/&#8217;)->Body->children(‘http://soap.sforce.com/2005/09/outbound&#8217;)->notifications->Notification->sObject->children(‘urn:sobject.enterprise.soap.sforce.com’)->CreatedDate point to the block between <sf:CreatedDate> and </sf:CreatedDate>. sd, the namespace, is defined in <sObject>, we need to use children and its urn to be able to access the value in <sf:CreatedDate>.

Test and Run

The full program as below. You need to save the xml files as the one I used in my program “OutboundMessage.xml” and put them in the same directory to make it work.

<?php
header("Content-Type: text/plain\r\n");

$xmlFile = 'OutboundMessage.xml';

$file = fopen( $xmlFile, 'r' );
$content = fread( $file, filesize($xmlFile) );
fclose( $file );

echo "XML ==============================".PHP_EOL.PHP_EOL;
echo $content;

echo PHP_EOL."Parsing ..............................".PHP_EOL.PHP_EOL;

$rcXML = simplexml_load_string($content);

echo '<Id>: '.$rcXML->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('http://soap.sforce.com/2005/09/outbound')->notifications->Notification->Id.PHP_EOL;

echo '<sf:Id>: '.$rcXML->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('http://soap.sforce.com/2005/09/outbound')->notifications->Notification->sObject->children('urn:sobject.enterprise.soap.sforce.com')->Id.PHP_EOL;

echo '<sf:CreatedDate>: '.$rcXML->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('http://soap.sforce.com/2005/09/outbound')->notifications->Notification->sObject->children('urn:sobject.enterprise.soap.sforce.com')->CreatedDate.PHP_EOL;

echo '<sf:LastName>: '.$rcXML->children('http://schemas.xmlsoap.org/soap/envelope/')->Body->children('http://soap.sforce.com/2005/09/outbound')->notifications->Notification->sObject->children('urn:sobject.enterprise.soap.sforce.com')->LastName.PHP_EOL;

echo PHP_EOL."End ==============================".PHP_EOL.PHP_EOL;
?>
XML with namespace parsed by SimpleXML
XML with namespace parsed by SimpleXML

Reference

  1. PHP.net: SimpleXML
  2. PHP.net: SimpleXMLElement::children
  3. PHP.net: SimpleXMLElement::xpath
  4. PHP.net: The SoapServer class
  5. PHP.net: XML Parser
  6. PHP.net: XMLReader
  7. Salesforce: Force.com Toolkit for PHP (Version 20.0)
  8. PHP.net: simplexml_load_string()
  9. StackOverflow: simplexml help how do I parse this?
  10. SitePoint: SimpleXML and namespaces by Kevin Yank
  11. w3schools.com: XML Namespaces – Default Namespaces
  12. w3schools.com: XML Namespaces – Uniform Resource Identifier (URI)

 

8 thoughts on “Parse XML with namespace by SimpleXML in PHP

  1. This article looks promising compared to the rest I’ve been seeing until I tried it on my own and won’t get the desired result.

    I’ll appreciate if you can help write the code to extract ReferenceNumber from this XML

    6297590
    100

    Thanks in anticipation.

    Liked by 1 person

  2. I’d been struggling with this – this article’s step by step of wading down the XML namespace tree really made it clear what I needed to do. I probably looked at 10 other articles/example before finding this one. Thank you for taking the time to bring some clarity to dealing with namespaces with SimpleXML.

    Liked by 1 person

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.