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
- the name comes with namespace, use children(‘namespace’) before the tag.
- the name doesn’t comes with namespace, use the tag name.
- the tag contains a xmlns, use children(‘namespace’) before the tag.
- 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->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;
$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/’)->Body point to the block between <soapenv:Body> and </soapenv:Body>. The definition of soapenv is “http://schemas.xmlsoap.org/soap/envelope/” which may be found in the second line of the xml file as xmlns:soapenv=”http://schemas.xmlsoap.org/soap/envelope/”. 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/’)->Body->children(‘http://soap.sforce.com/2005/09/outbound’)->notifications point to the block between <notifications xmlns=”http://soap.sforce.com/2005/09/outbound”> and </notifications>. The xmlns=”http://soap.sforce.com/2005/09/outbound” means it is the default namespace for notifications. That’s why we have to use children().
$rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/’)->Body->children(‘http://soap.sforce.com/2005/09/outbound’)->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/’)->Body->children(‘http://soap.sforce.com/2005/09/outbound’)->notifications->Notification->Id
$rcXML->children(‘http://schemas.xmlsoap.org/soap/envelope/’)->Body->children(‘http://soap.sforce.com/2005/09/outbound’)->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/’)->Body->children(‘http://soap.sforce.com/2005/09/outbound’)->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; ?>
Reference
- PHP.net: SimpleXML
- PHP.net: SimpleXMLElement::children
- PHP.net: SimpleXMLElement::xpath
- PHP.net: The SoapServer class
- PHP.net: XML Parser
- PHP.net: XMLReader
- Salesforce: Force.com Toolkit for PHP (Version 20.0)
- PHP.net: simplexml_load_string()
- StackOverflow: simplexml help how do I parse this?
- SitePoint: SimpleXML and namespaces by Kevin Yank
- w3schools.com: XML Namespaces – Default Namespaces
- w3schools.com: XML Namespaces – Uniform Resource Identifier (URI)
Thanks so much! Saved me today.
LikeLiked by 1 person
Dear James,
Glad to know it helps!
Have a nice day!
Best regards,
Amigo
LikeLike
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.
LikeLiked by 1 person
Dear Oluwarufus,
I suggest you read the thread: salesforce Outbound message Listener and use tanakahisateru / salesforce-wfoutbound-sample.php instead.
This post is to explain how it works. But my friend Hisateru Tanaka has create a better reusable object/class for this. I think it is more practical than mine. 🙂
BTW, i didn’t see the ReferenceNumber you have mentioned in my XML.
Wish it helps!
Best regards,
Amigo
LikeLike
Thank you very much you saved my life today !
LikeLiked by 1 person
Dear Samih,
Thank you for your feedback!
Have a nice day!
Best regards,
Amigo
LikeLiked by 1 person
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.
LikeLiked by 1 person
Thanks so much! Saved me today.
LikeLike