simplexml and using relative path - php

I am trying to get to a part in an xml response, without following the whole path. Now I know that xpath has search abilities, but somehow I dont understand it... :(
The XML i am parsing is this:
<?xml version="1.0" encoding="utf-8"?>
<soapEnvelope xmlns:soap="http://www.w3.org/2003/05/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">
<envHeader xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<wsaAction>http://www.rechtspraak.nl/namespaces/cir01/searchUndertakingResponse</wsaAction>
<wsaMessageID>urn:uuid:11f7d4cd-2280-4298-85eb-dadf5bd743f1</wsaMessageID>
<wsaRelatesTo>urn:uuid:59630fbd-b990-4020-9c1c-822c58186d96</wsaRelatesTo>
<wsaTo>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsaTo>
<wsseSecurity>
<wsuTimestamp wsu:Id="Timestamp-df25f141-fed2-47ed-967e-93cd04d1c8f2">
<wsuCreated>2011-04-02T06:52:52Z</wsuCreated>
<wsuExpires>2011-04-02T06:57:52Z</wsuExpires>
</wsuTimestamp>
</wsseSecurity>
</envHeader>
<soapBody>
<searchUndertakingResponse xmlns="http://www.rechtspraak.nl/namespaces/cir01"><searchUndertakingResult>
<publicatieLijst xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" extractiedatum="2011-04-02T08:52:51" xmlns="http://www.rechtspraak.nl/namespaces/inspubber01">
<publicatieKenmerk>sgr.10.787.F.1300.1.10</publicatieKenmerk>
<publicatieKenmerk>utr.10.585.F.1300.1.10</publicatieKenmerk>
</publicatieLijst>
</searchUndertakingResult>
</searchUndertakingResponse>
</soapBody>
</soapEnvelope>
And I am looking for these values: <publicatieKenmerk>sgr.10.787.F.1300.1.10</publicatieKenmerk> <publicatieKenmerk>utr.10.585.F.1300.1.10</publicatieKenmerk>
Now this works:
$lijst = $results->soapBody->searchUndertakingResponse->searchUndertakingResult->publicatieLijst->publicatieKenmerk;
foreach ($lijst AS $kenmerk) {
echo $kenmerk."<BR>";
}
But I dont want to use this, as I need to be flexible for other results. and cannot rely on
searchUndertakingResponse->searchUndertakingResult
So I was hoping to use xpath to get there, but this doesnt work:
$lijst = $results->xpath('//publicatieKenmerk');
foreach($lijst as $kenmerk) {
echo $kenmerk."<br />";
}
But I thought relative would work as well... any ideas?

You'll notice that your <publicatieLijst> node has a default namespace set: xmlns="http://www.rechtspraak.nl/namespaces/inspubber01" which means that <publicatieKenmerk> exists that namespace. That's why //publicatieKenmerk won't find it, you have to search in the right namespace.
For that, you can register the namespace with your own prefix and use that prefix in the following XPath query, like this:
$soapEnvelope = simplexml_load_string($xml);
$soapEnvelope->registerXPathNamespace(
'inspubber01',
'http://www.rechtspraak.nl/namespaces/inspubber01'
);
foreach ($soapEnvelope->xpath('//inspubber01:publicatieKenmerk') as $publicatieKenmerk)
{
echo $publicatieKenmerk, "\n";
}

can you try to change on they way it loops?
while(list( , $node) = each($lijst)) {
echo 'kenmerk: ',$node,"\n";
}

Related

php SimpleXML get full namespace attributes [duplicate]

I'd like to get the content of the attribute xsi:schemaLocation. It's works perfectly with getElementsByTagName in php (and foreach after) but it's ugly, right ?
How to get the same content with a simple Xpath query ?
Here a short example of the xml content :
<?xml version="1.0" encoding="utf-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1.0" creator="blabla" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd" xmlns="http://www.topografix.com/GPX/1/0">
...
</gpx>
Thanks!
Typically you need to register the namespaces you want to use with the XPath library first. Then you can query the attribute by including namespace prefix along with the name.
So let's assume you're using DOMXPath, you might register the following namespaces:
$xpath = new DOMXPath($doc);
$xpath->registerNamespace("xsi","http://www.w3.org/2001/XMLSchema-instance");
$xpath->registerNamespace("gpx", "http://www.topografix.com/GPX/1/0");
And then you can query the schemaLocation attribute with something like this:
$xpath->query("/gpx:gpx/#xsi:schemaLocation",$doc);
Using the SimpleXMLElement class you can easily get the attribute xsi:schemaLocation's value:
<?php
$xml = <<<XML
<?xml version="1.0" encoding="utf-8"?>
<gpx xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" version="1.0" creator="blabla" xsi:schemaLocation="http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd http://www.groundspeak.com/cache/1/0/1 http://www.groundspeak.com/cache/1/0/1/cache.xsd" xmlns="http://www.topografix.com/GPX/1/0">
</gpx>
XML;
$sxe = new SimpleXMLElement($xml);
$schemaLocation = $sxe->attributes('xsi', true)->schemaLocation;
echo (string) $schemaLocation;

cant get values from xml with php

I must be missing something simple here and it's driving me crazy.
I'm making a call to a web services api and getting back some xml:
<?xml version="1.0" encoding="utf-16"?>
<MTSMember xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<LastName>Smith</LastName>
<FirstName>john</FirstName>
<MemberNo>xxxxxxxx</MemberNo>
<Club>None</Club>
<JoinDate>2013-05-14</JoinDate>
<Email>email#email.com</Email>
</MTSMember>
I then need to process this xml to get the email address. but I'm just getting an empty result using the code below:
$xml_result = simplexml_load_string($xml_string_above);
echo $xml_result->MTSMember[0]->Email;
Can someone point me in the right direction. I've read through several other answers trying out various solutions, but can't seem to get it to work.
Edit: This was the last tutorial i tried out http://blog.teamtreehouse.com/how-to-parse-xml-with-php5
That should be:
echo $xml_result->Email;
Because simplexml_load_string() is loading MTSMember as main SimpleXMLElement.
Codepad Example
Try this
$xml=new SimpleXMLElement($str);
$result=$xml->xpath('//Email');
foreach ($result as $Email)
echo $Email . "<br>";
simply
$xml=new SimpleXMLElement($str);
echo $xml[0]->Email;
update:
The below just for your comment , the whole what i tried
$str='<?xml version="1.0" encoding="utf-8"?>
<MTSMember xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<LastName>Smith</LastName>
<FirstName>john</FirstName>
<MemberNo>xxxxxxxx</MemberNo>
<Club>None</Club>
<JoinDate>2013-05-14</JoinDate>
<Email>email#email.com</Email>
</MTSMember>';
$xml=new SimpleXMLElement($str);
echo $xml[0]->Email;
//OR
$xml=new SimpleXMLElement($str);
$result=$xml->xpath('//Email');
foreach ($result as $Email)
echo $Email . "<br>";
Be happy :)
This should work fine
$xml = new SimpleXMLElement($xml_string_above);
$dc = $xml->email;
echo $dc;

How to parse SOAP in order to list all Request and Object names

I'm able to parse an XML SOAP when I know Namespace and Request name.
Because I have different kind of SOAP requests, I would like to get the Request name in the SOAP file . Extract of a part of my SOAP:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="http://schema.example.com"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
>
<SOAP-ENV:Body>
**<ns1:SendMailling>**
<campagne xsi:type="ns1:Campaign"><ActivateDedup xsi:nil="true"/><BillingCode xsi:nil="true"/><DeliveryFax xsi:type="ns1:DeliveryFax"/>
<DeliveryMail xsi:type="ns1:DeliveryMail">
...
PHP Code:
if(is_file($file))
{
$content=file_get_contents($file);
$xml = simplexml_load_string($content);
$xml->registerXPathNamespace('ns1', 'http://schema.example.com');
foreach ($xml->xpath('\\SOAP-ENV:') as $item)
{
//certainly the bad way?
echo "<pre>";
print_r($item);
echo "</pre>";
}
echo "<pre>";
print_r($xml);
echo "</pre>";
}
I get no result... I would like to make appears : 'SendMailling' (identify the request name)
When I specifically specify
//foreach($xml->xpath('//ns1:SendMailling') as $item)
there is no problems.
I tried foreach($xml->xpath('//ns1') as $item)
and $xml->xpath('//SOAP-ENC'), $xml->xpath('//Body') but...
I had problems to understand your questions so this probably is not the answer.
If I understand you right, you want to select all Element Nodes that are a direct children of <SOAP-ENV:Body> and that are in the ns1 / http://schema.example.com namespace.
You already have registered the namespace prefix to be used with SimpleXMLElement::xpath:
$xml->registerXPathNamespace('ns1', 'http://schema.example.com');
You have not yet registered the SOAP-ENV / http://schemas.xmlsoap.org/soap/envelope/ namespace as far as I can see.
In XPath to match an element you can specify it's namespace. There are multiple ways how this is done:
* All elements in any namespace.
prefix:* All elements in namespace "prefix" (registered prefix)
prefix:local Only "local" elements in namespace "prefix"
For example to select all elements with the ns1 prefix:
//ns1:*
You might want to limit this because you only want direct children withn <SOAP-ENV:Body> so register that namespace with the SOAP-ENV prefix and extend the previous xpath:
/SOAP-ENV:Body/ns1:*
This should then have all those elements you're looking for.
(OP:) Thanks again, when I do a
foreach ($xml->xpath('//SOAP-ENV:Body/ns1:*') as $item) {
echo $item->getName() . "<br>";
}
All is ok I get the request Name 'SendMailling'.

Can't read XML with SimpleXML

I'm receiving through cUrl an XML generated with PHP with this code:
$c = curl_init("http://www.domain.com/script.php");
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
$xmlstr = curl_exec($c);
If I echo the $xmlstr variable it shows the following:
<?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">
<soap:Body>
<XGetPasoParadaREGResponse xmlns="http://tempuri.org/">
<XGetPasoParadaREGResult>
<PasoParada><cabecera>false</cabecera>
<e1>
<minutos>1</minutos>
<metros>272</metros>
<tipo>NORMAL</tipo>
</e1>
<e2>
<minutos>7</minutos>
<metros>1504</metros>
<tipo>NORMAL</tipo>
</e2><linea>28
</linea>
<parada>376</parada>
<ruta>PARQUE ALCOSA</ruta>
</PasoParada>
</XGetPasoParadaREGResult><status>1</status></XGetPasoParadaREGResponse>
</soap:Body>
</soap:Envelope>
Which is correct and the same generated at the script. However, if I try to do the following
$xml = simplexml_load_string($xmlstr);
if (!is_object($xml))
throw new Exception('Reading XML error',1001);
echo $xml->e1->minutos;
Doesn't show anything and print_r the object prints an empty object. What could be wrong?
You have two main problems, firstly you aren't taking into account any namespaces (e.g. soap) and secondly you aren't traversing the XML hierarchy to get at the desired element.
The "simple" way would be:
$minutos = (int) $xml
->children('soap', TRUE) // <soap:*>
->Body // <soap:Body>
->children('') // <*> (default/no namespace prefix)
->XGetPasoParadaREGResponse // <XGetPasoParadaREGResponse>
->XGetPasoParadaREGResult // <XGetPasoParadaREGResult>
->PasoParada // <PasoParada>
->e1 // <e1>
->minutos; // <minutos> yay!
You could also make an XPath query for the value:
// Our target node belongs in this namespace
$xml->registerXPathNamespace('n', 'http://tempuri.org/');
// Fetch <minutos> elements children to <e1>
$nodes = $xml->xpath('//n:e1/n:minutos');
// $nodes will always be an array, get the first item
$minutos = (int) $nodes[0];
For what it is worth, the PHP Manual contains basic usage examples covering how to traverse the XML structure and the specific pages for children() and registerXPathNamespace() show you how to work with namespaced elements with examples.
Finally, as you have seen, the output from print_r()/var_dump() with SimpleXML is not always very helpful at all! The best way to see what you've got is to echo saveXML() which will display the XML for a given element.
Try the children method:
foreach ($xml->children("soap", true) as $k => $v) {
echo $k . ":\n";
var_dump($v);
}

PHP simplexml: why does xpath stop working?

A strange thing happened after a supplier changed the XML header a bit. I used to be able to read stuff using xpath, but now I can't even get a reply with
$xml->xpath('/');
They changed it from this...
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE NewsML SYSTEM "http://www.newsml.org/dl.php?fn=NewsML/1.2/specification/NewsML_1.2.dtd" [
<!ENTITY % nitf SYSTEM "http://www.nitf.org/IPTC/NITF/3.4/specification/dtd/nitf-3-4.dtd">
%nitf;
]>
<NewsML>
...
to this:
<?xml version="1.0" encoding="iso-8859-1"?>
<NewsML
xmlns="http://iptc.org/std/NewsML/2003-10-10/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://iptc.org/std/NewsML/2003-10-10/ http://www.iptc.org/std/NewsML/1.2/specification/NewsML_1.2.xsd http://iptc.org/std/NITF/2006-10-18/ http://contentdienst.pressetext.com/misc/nitf-3-4.xsd"
>
...
Most likely this is because they've introduced a default namespace (xmlns="http://iptc.org/std/NewsML/2003-10-10/") into their document. SimpleXML's support for default namespaces is not very good, to put it mildly.
Can you try to explicitly register a namespace prefix:
$xml->registerXPathNamespace("n", "http://iptc.org/std/NewsML/2003-10-10/");
$xml->xpath('/n:NewsML');
You would have to adapt your XPath expressions to use the "n:" prefix on every element. Here is some additional info: http://people.ischool.berkeley.edu/~felix/xml/php-and-xmlns.html.
EDIT: As per the spec:
The registerXPathNamespace() function creates a prefix/ns context for the next XPath query.
This means it would have to be called before every XPath query, thus a function to wrap XPath queries would be the natural thing to do:
function simplexml_xpath_ns($element, $xpath, $xmlns)
{
foreach ($xmlns as $prefix_uri)
{
list($prefix, $uri) = explode("=", $prefix_uri, 2);
$element->registerXPathNamespace($prefix, $uri);
}
return $element->xpath($xpath);
}
Usage:
$xmlns = ["n=http://iptc.org/std/NewsML/2003-10-10/"];
$result = simplexml_xpath_ns($xml, '/n:NewsML', $xmlns);

Categories