SimpleXmlElement and XPath, getting empty array()
The issue is likely the default namespace. See
SimpleXMLElement::registerXPathNamespace
Creates a prefix/ns context for the next XPath query
Example:
$sxe->registerXPathNamespace('x', 'http://checkout.google.com/schema/2');
$result = $sxe->xpath('//x:notifications');
As an alternative if there is no other namespaces, simply remove the default namespace with
str_replace('xmlns="http://checkout.google.com/schema/2"', '', $raw_xml);
before feeding the XML to your SimpleXmlElement.
Why does SimpleXML XPath return an empty array?
xmlns="http://schema.infor.com/InforOAGIS/2"
This declares a default namespace which you do need to register via registerXPathNamespace()
and address the elements using the prefix registered.
Example:
$xml = simplexml_load_file("example.xml");
$xml->registerXPathNamespace('ns', "http://schema.infor.com/InforOAGIS/2");
$displayIds = $xml->xpath('/ns:SyncCustomerPartyMaster/ns:DataArea/ns:CustomerPartyMaster/ns:PartyIDs/ns:DisplayID');
foreach ($displayIds as $displayId) {
echo $displayId;
}
Output:
10100
SimpleXML xpath has empty element
Ok, so I've figured out what was happening, and leaving this here in the hopes that anyone who stumbles across the same issue will find this answer helpful. Thanks to IMSoP for helping me get to the answer.
First, SimpleXML's xpath()
method returns an array, not a XML object, which makes life difficult from the outset. Second of all, if you look at the <notifications>
tag, you will see it uses the outbound
namespace, which in my example I aliased to ob
. As such, both Notification
and sObject
are also considered to be part of the same namespace as they are children of the namespaced element, which means the correct xpath is
//soapenv:Envelope/soapenv:Body/ob:notifications/ob:Notification/ob:sObject
Which, as mentioned above, in SimpleXML will return an array, so grab the first index, and use the provided namespace url for sObject in the children()
method call and you will get your data.
So using the above example XML
<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>00DN0000000XXXXXXX</OrganizationId>
<ActionId>04kN0000000XXXXXXX</ActionId>
<SessionId xsi:nil="true"/>
<EnterpriseUrl>https://cs6.salesforce.com/services/Soap/c/38.0/00DN0000000XXXXXXX</EnterpriseUrl>
<PartnerUrl>https://cs6.salesforce.com/services/Soap/u/38.0/00DN0000000XXXXXXX</PartnerUrl>
<Notification>
<Id>00DN0000000XXXXXXX</Id>
<sObject xsi:type="sf:Account"
xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
<sf:Id>00DN0000000XXXXXXX</sf:Id>
<sf:FirstName>Bill</sf:FirstName>
<sf:LastName>Jack</sf:LastName>
<sf:PersonEmail>abc@xyz.net</sf:PersonEmail>
<sf:PersonMobilePhone>0400000000</sf:PersonMobilePhone>
</sObject>
</Notification>
</notifications>
</soapenv:Body>
</soapenv:Envelope>
We can get the data by doing the following:
$xml = simplexml_load_string($xmlString);
$xml->registerXPathNamespace('soapenv', 'http://schemas.xmlsoap.org/soap/envelope/');
$xml->registerXPathNamespace('ob', 'http://soap.sforce.com/2005/09/outbound');
$sObject = $xml->xpath('//soapenv:Envelope/soapenv:Body/ob:notifications/ob:Notification/ob:sObject')[0];
$data = $sObject->children('urn:sobject.enterprise.soap.sforce.com');
var_dump($data);
SimpleXML returns empty arrays
To search for a particular lat
do this:
<?php
$xml = simplexml_load_file("http://maps.googleapis.com/maps/api/geocode/xml?address=Brussels,Belgium&sensor=false");
$lat= $xml->xpath("/GeocodeResponse/result/geometry/location/lat");
print_r($lat);
?>
For all lat
occurrences specify the path of //lat
.
PHP xml to array - how to get rid of empty tags?
Found it out my self. Took a while but works perfectly.
/**
* @param array|\SimpleXMLElement[]|\SimpleXMLElement $data .
*
* @return array
*/
protected function emptyNodesToNull($data)
{
if ($data instanceof \SimpleXMLElement and $data->count() === 0) {
// is empty object like
// SimpleXMLElement::__set_state(array())
// which was f.e. a <foo/> tag
// or
// SimpleXMLElement::__set_state(array(0 => ' ',))
// which was f.e. a <foo> </foo> (with white space only)
return null;
}
$data = (array)$data;
foreach ($data as &$value) {
if (is_array($value) or $value instanceof \SimpleXMLElement) {
$value = $this->emptyNodesToNull($value);
} else {
// $value is the actual value of a node.
// Could do further checks here.
}
}
return $data;
}
My tests did exactly what i expected
and returns imo exactly what you can expect from a xmlToArray method.
I mean we wont be able to handle attributes, but this is not the requirement.
Test:
$xml
= '<?xml version="1.0"?>
<Envelope>
<a/><!-- expecting null -->
<foo>
<b/><!-- expecting null -->
<bar>
<baz>Hello</baz>
<!-- expecting here an array of 2 x null -->
<c/>
<c/>
</bar>
</foo>
<foo>
<bar>
<baz>Hello Again</baz>
<d> </d><!-- expecting null -->
<item>
<firstname>Foo</firstname>
<email></email><!-- expecting null -->
<telephone/><!-- expecting null -->
<lastname>Bar</lastname>
</item>
<item>
<firstname>Bar</firstname>
<email>0</email><!-- expecting value 0 (zero) -->
<telephone/><!-- expecting null -->
<lastname>Baz</lastname>
</item>
<!-- expecting array of values 1, 2 null, 4 -->
<number>1</number>
<number>2</number>
<number></number>
<number>4</number>
</bar>
</foo>
</Envelope>';
$xml = new \SimpleXMLElement($xml);
$array = $class::emptyNodesToNull($xml);
Returns:
[
'Envelope' => [
'a' => null,
'foo' => [
0 => [
'b' => null,
'bar' => [
'baz' => 'Hello',
'c' => [
0 => null,
1 => null,
],
],
],
1 => [
'bar' => [
'baz' => 'Hello Again',
'd' => null,
'item' => [
0 => [
'firstname' => 'Foo',
'email' => null,
'telephone' => null,
'lastname' => 'Bar',
],
1 => [
'firstname' => 'Bar',
'email' => '0',
'telephone' => null,
'lastname' => 'Baz',
],
],
'number' => [
0 => '1',
1 => '2',
2 => null,
3 => '4',
],
],
],
],
],
];
simplexml_load_file with xPath returns empty array
It might be rather easy, but I guess it is also the most common mistake in the history of XML: You are forgetting namespaces!
A lot of elements in the given XML are changing the default namespace and you have to consider that in your XPath.
You can first register your namespace like so:
$xml->registerXPathNamespace('y', 'http://maps.yandex.ru/ymaps/1.x');
$xml->registerXPathNamespace('a', 'http://maps.yandex.ru/attribution/1.x');
and then you can query your data:
$xml->xpath('//y:ymaps/y:GeoObjectCollection');
simpleXML returns empty element
How can I access all
<a:article>
in<f:articles>
?
To me simpleXML seems a bit clumsy when working with namespaces (but then again, I'm not a PHP coder). You could use the children
method but in my opinion using XPath is simpler.
$feed->registerXPathNamespace('f', 'http://www.bbgo.de/feedxml/feed');
$feed->registerXPathNamespace('a', 'http://www.bbgo.de/feedxml/article');
$article = feed->xpath("/f:feed/f:articles/a:article");
First you need to register the namespaces with some prefixes (prefixes don't need to match the ones used in the XML document but namespace URIs must match) and then use these prefixes in your XPath expression that selects your target. Without the element name prefixes the XPath expression would look for elements that are not in any namespace.
Why $feed array is not built?
It's hard to guess a reason for this. Some things you could test:
- is the string
$xml
a well-formed xml document - does creating a SimpleXMLElement with explicit string parameter work
$feed = new SimpleXMLElement('<root><child/></root>');
- can you load your document using
simplexml_load_file($yourXMLfile);
orsimplexml_load_string($yourXMLstring);
SimpleXMLElement and xpath
The declaration in the second line of your XML gives two XML namespaces:
<entry xmlns="http://www.w3.org/2005/Atom" xmlns:zapi="http://zotero.org/ns/api">
The first xmlns
attribute, with no colon-separated suffix, sets the "default" namespace for the entry
element as being http://www.w3.org/2005/Atom
, and descendant nodes with no prefix are in that namespace by default. To be able to access these elements with a "default" namespace using XPath, you need to set a namespace using registerXPathNamespace
, and then perform your query, prefixing the element you're looking for (link
) with the namespace:
$xml->registerXPathNamespace('d', 'http://www.w3.org/2005/Atom');
$php_up = $xml->xpath("//d:link[@rel='up']");
var_dump($php_up);
Output:
array(1) {
[0] =>
class SimpleXMLElement#2 (1) {
public $@attributes =>
array(3) {
'rel' =>
string(2) "up"
'type' =>
string(20) "application/atom+xml"
'href' =>
string(9) "some link"
}
}
}
Using Xpath in php, return an empty array
This line is incorrect:
$data->registerXPathNamespace('c',$html);
$html
is the URL that you are querying, but in this line you need to specify the namespace of the XML you are using. That namespace is:
http://musicbrainz.org/ns/mmd-2.0#
So use this:
$data->registerXPathNamespace('c','http://musicbrainz.org/ns/mmd-2.0#');
Related Topics
PHP String Concatenation - "$A $B" VS $A . " " . $B - Performance
In Flutter Web Getting 'Xmlhttprequest' Error While Making Http Call
PHP Xpath - Find Element with a Value and Also Get Elements Before and After Element
Converting a Carbon Date to MySQL Timestamp
Why Are PHP's MySQL_ Functions Deprecated
How to Start a Windows Gui Program Using PHP
How to Check If the Request Came from Mobile or Computer in PHP
Output (Echo/Print) Everything from a PHP Array
What Is Laravel Render() Method For
Differencebetween .= and += in PHP
How to Set PHP's Auto_Prepend_File Directive Per Directory
Understanding PHP "Out of Memory" Error
Variable Variables in PHP - What Is Their Purpose
How to Get Average of Column Values in Laravel
When Should I Use Closecursor() for Pdo Statements
How to Get List of Supported Encodings by Iconv Library in PHP