Parsing complex xml with simplexml - php

I have got this xml structure below. For every 'locatie' (in every 'land') I need the 'id' value and it's 'sneeuwkwaliteit'.
My effort so far, does not return the value of 'sneeuwkwaliteit':
$sneeuw = simplexml_load_file('ski.xml');
echo $sneeuw->land[0]->locaties[0]->sneeuw->{'ski'}->sneeuwkwaliteit;
<sneeuw>
<aanmaak_tijd>09-01-2016 07:48</aanmaak_tijd>
<landen>
<land id="Andorra">
<locaties>
<locatie id="Arinsal/Pal">
<ski>
<datum_tijd>09-01-2016</datum_tijd>
<sneeuwhoogte_dal>40</sneeuwhoogte_dal>
<sneeuwhoogte_berg>70</sneeuwhoogte_berg>
<sneeuwkwaliteit>De aanwezige sneeuw is poedersneeuw.</sneeuwkwaliteit>
<datum_laatste_sneeuwval>07-01-2016</datum_laatste_sneeuwval>
<hoeveelheid_laatste_sneeuwval>20</hoeveelheid_laatste_sneeuwval>
<totaal_aantal_liften>25</totaal_aantal_liften>
<aantal_liften_geopend>25</aantal_liften_geopend>
</ski>
</locatie>
</locaties>
</land>
</landen>
</sneeuw>

Just carefully follow the path from the root of your XML to the target element :
$xml = <<<XML
<sneeuw>
<aanmaak_tijd>09-01-2016 07:48</aanmaak_tijd>
<landen>
<land id="Andorra">
<locaties>
<locatie id="Arinsal/Pal">
<ski>
<datum_tijd>09-01-2016</datum_tijd>
<sneeuwhoogte_dal>40</sneeuwhoogte_dal>
<sneeuwhoogte_berg>70</sneeuwhoogte_berg>
<sneeuwkwaliteit>De aanwezige sneeuw is poedersneeuw.</sneeuwkwaliteit>
<datum_laatste_sneeuwval>07-01-2016</datum_laatste_sneeuwval>
<hoeveelheid_laatste_sneeuwval>20</hoeveelheid_laatste_sneeuwval>
<totaal_aantal_liften>25</totaal_aantal_liften>
<aantal_liften_geopend>25</aantal_liften_geopend>
</ski>
</locatie>
</locaties>
</land>
</landen>
</sneeuw>
XML;
$sneeuw = simplexml_load_string($xml);
echo $sneeuw->landen[0]->land[0]->locaties[0]->locatie[0]->ski[0]->sneeuwkwaliteit;
eval.in demo
output :
De aanwezige sneeuw is poedersneeuw.
For more complex query against XML, look into XPath, which has it's own specification. Then you can execute XPath expression using SimpleXMLElement::xpath() function.
UPDATE :
You can use XPath, as mentioned above, to iterate through all sneeuwkwaliteit elements located in the same path :
....
$result = $sneeuw->xpath('/sneeuw/landen/land/locaties/locatie/ski/sneeuwkwaliteit');
foreach($result as $r){
echo $r ."<br>";
}

Related

Query XML File using PHP for Values

I am currently working on a project that requires me to query an XML file like php to return a value that matches the request. Take a look at the XML:
<ENVELOPE>
<MASTER>
<STKDETNAME>004-011</STKDETNAME>
<STKPNO>PTN771</STKPNO>
<STKPRICE></STKPRICE>
<STKOPBAL>500</STKOPBAL>
</MASTER>
<MASTER>
<STKDETNAME>004-012</STKDETNAME>
<STKPNO>PTN772</STKPNO>
<STKPRICE></STKPRICE>
<STKOPBAL>500</STKOPBAL>
</MASTER>
<MASTER>
<STKDETNAME>004-013</STKDETNAME>
<STKPNO>PTN773</STKPNO>
<STKPRICE></STKPRICE>
<STKOPBAL>1000</STKOPBAL>
</MASTER>
<MASTER>
<STKDETNAME>004-014</STKDETNAME>
<STKPNO>PTN774</STKPNO>
<STKPRICE></STKPRICE>
<STKOPBAL>1000</STKOPBAL>
</MASTER>
<MASTER>
<STKDETNAME>004-015</STKDETNAME>
<STKPNO>PTN775</STKPNO>
<STKPRICE>400</STKPRICE>
<STKOPBAL>1000</STKOPBAL>
</MASTER>
</ENVELOPE>
Now, I want to get the STKPRICE AND STKOPBAL for a SKTPNO= PTN773. This is what i have seen so far, but i don't know how to get the two values. I am new to XML.
$file = 'stocksum.xml';//same file as above
$xmlfile = simplexml_load_file($file);
$partno = PTN775;
$fnd = $xmlfile->xpath('/ENVELOPE/MASTER/STKPNO[.="$partno"]');
There are a couple of issues with the code which are just syntax problems, these are the partno needing quotes and when building the XPath expression, you use single quotes so it doesn't insert the actual part number.
BUT to get to your actual problem, if you change your XPath to the one used here, this will find the <MASTER> element whose <STKPNO> is the one your after. So then you can refer to the elements withing the <MASTER> element using standard SimpleXML object notation...
$partno = 'PTN775';
$fnd = $xmlfile->xpath('/ENVELOPE/MASTER[STKPNO="'.$partno.'"]');
echo $fnd[0]->STKPRICE.PHP_EOL;
Note that as xpath() returns a list of matches, I use $fnd[0] to get the first one.
Code which also has a check to see if the part actually exists...
$xmlfile = simplexml_load_file($file);
$partno = 'PTN7751';
$fnd = $xmlfile->xpath('/ENVELOPE/MASTER[STKPNO="'.$partno.'"]');
if ( count($fnd) == 0 ) {
echo "Not found";
}
else {
echo $fnd[0]->STKPRICE.PHP_EOL;
}

Modify xml nodes using DOM or SIMPLE XML?

I have source XML here: http://www.grilykrby.cz/rss/pf-heureka.xml. I want to use this xml feed and create another modified on my own server. I would like to change every node CATEGORYTEXT which contains word Prislusenstvi. I just tried something but I got only the listing of all categories without changing XML :-(
Here is the example of my code. The row $kategorie="nejaka kategorie"; doesn't work.
<?php
$file = "http://www.grilykrby.cz/rss/pf-heureka.xml";
$xml=simplexml_load_file($file);
foreach ($xml->xpath('//SHOPITEM/CATEGORYTEXT') as $kategorie) {
echo $kategorie."<br />";
$kategorie="nejaka kategorie";
}
file_put_contents('test.xml', $xml->asXML());
?>
$kategorie is just a temp variable used in the loop which contains a copy of the data returned by xpath query. You would need to actually set the value directly in the $xml object.
I would personally also consider doing a str_replace or preg_replace within the XML content itself before parsing it into a simpleXML object.
Final Accepted Answer
<?php
$xml = simplexml_load_file('http://www.grilykrby.cz/rss/pf-heureka.xml');
$i=0;
foreach($xml -> SHOPITEM as $polozka) {
if ($polozka -> CATEGORYTEXT == "Příslušenství") $xml -> SHOPITEM[$i] -> CATEGORYTEXT = "Some other text";
$i++;
}
?>

creating multiple xml nodes with same namespaces in php

I have the following code
$dom = new DOMDocument('1.0', 'utf-8');
$headerNS = $dom->createElementNS('http://somenamespace', 'ttauth:authHeader');
$accesuser = $dom->createElementNS('http://somenamespace', 'ttauth:Accessuser','aassdd');
$accesscode = $dom->createElementNS('http://somenamespace', 'ttauth:Accesscode','aassdd');
$headerNS->appendChild($accesuser);
$headerNS->appendChild($accesscode);
echo "<pre>";
echo ($dom->saveXML($headerNS));
echo "</pre>";
IT will produce the following xml as output
<?xml version="1.0" ?>
<ttauth:authHeader xmlns:ttauth="http://somenamespace">
<ttauth:Accessuser>
ApiUserFor136
</ttauth:Accessuser>
<ttauth:Accesscode>
test1234
</ttauth:Accesscode>
</ttauth:authHeader>
But I want the following output
<ttauth:authHeader xmlns:ttauth="http://somenamespace">
<ttauth:Accessuser xmlns:ttauth="http://somenamespace">
aassdd
</ttauth:Accessuser>
<ttauth:Accesscode xmlns:ttauth="somenamespace">
aassdd
</ttauth:Accesscode>
</ttauth:authHeader>
See the xmlns is not included in elements other than root element but I want xmlns to be included in all elements Is there anything I am doing wrong ??
Probably the PHP parser does not add renaming of the same namespace "http://somenamespace" with the same prefix "ttauth" because it is redundant. Both xmls you shown ( the output and expected ) are equivalent. If you want to be sure you have the namespaces attributes as you want, you should add them manually by using addAtribute - http://www.php.net/manual/en/domdocument.createattribute.php. See the following code snippet:
$domAttribute = $domDocument->createAttribute('xmlns:ttauth');
$domAttribute->value = 'http://somenamespace';
$accessuser->appendChild($domAttribute);
Hope it helps
instead of using
$accesuser = $dom->createElementNS('http://somenamespace', 'ttauth:Accessuser','aassdd');
I used
$accesuser = $dom->createElement('http://somenamespace', 'ttauth:Accessuser','aassdd');
and then
$accesuser->setAttribute('xmlns:ttauth', ('http://somenamespace');
it works fine for any number of nodes

Simple xpath question that drives me crazy

below is the structure of a feed I managed to print the content using this xpath
$xml->xpath('/rss/channel//item')
the structure
<rss><channel><item><pubDate></pubDate><title></title><description></description><link></link><author></author></item></channel></rss>
However some of my files follow this structure
<feed xmlns="http://www.w3.org/2005/Atom" .....><entry><published></published><title></title><description></description><link></link><author></author></entry></feed>
and I guessed that this should be the xpath to get the content of entry
$xml->xpath('/feed//entry')
something that proved me wrong.
My question is what is the right xpath to use? Am i missing something else ?
This is the code
<?php
$feeds = array('http://feeds.feedburner.com/blogspot/wSuKU');
$entries = array();
foreach ($feeds as $feed) {
$xml = simplexml_load_file($feed);
$entries = array_merge($entries, $xml->xpath('/feed//entry'));
}
echo "<pre>"; print_r($entries); echo"</pre>";
?>
try this:
$xml->registerXPathNamespace('f', 'http://www.w3.org/2005/Atom');
$xml->xpath('/f:feed/f:entry');
If you want a single XPath expression that will work when applied to either an RSS or an ATOM feed, you could use either of the following XPath expressions:
This one is the most precise, but also the most verbose:
(/rss/channel/item
| /*[local-name()='feed' and namespace-uri()='http://www.w3.org/2005/Atom']
/*[local-name()='entry' and namespace-uri()='http://www.w3.org/2005/Atom'])
This one ignores the namespace of the ATOM elements and just matches on their local-name():
(/rss/channel/item | /*[local-name()='feed']/*[local-name()='entry'])
This one is the most simple, but the least precise and the least efficient:
/*//*[local-name()='item' or local-name()='entry']

Get child elements in xml with PHP

I have an xml file that I need to parse through and get values. Below is a snippit of xml
<?xml version="1.0"?>
<mobile>
<userInfo>
</userInfo>
<CATALOG>
<s0>
<SUB0>
<DESCR>Paranormal Studies</DESCR>
<SUBJECT>147</SUBJECT>
</SUB0>
</s0>
<sA>
<SUB0>
<DESCR>Accounting</DESCR>
<SUBJECT>ACCT</SUBJECT>
</SUB0>
<SUB1>
<DESCR>Accounting</DESCR>
<SUBJECT>ACCTG</SUBJECT>
</SUB1>
<SUB2>
<DESCR>Anatomy</DESCR>
<SUBJECT>ANATOMY</SUBJECT>
</SUB2>
<SUB3>
<DESCR>Anthropology</DESCR>
<SUBJECT>ANTHRO</SUBJECT>
</SUB3>
<SUB4>
<DESCR>Art</DESCR>
<SUBJECT>ART</SUBJECT>
</SUB4>
<SUB5>
<DESCR>Art History</DESCR>
<SUBJECT>ARTHIST</SUBJECT>
</SUB5>
</sA>
So, I need to grab all the child elements of <sA> and then there are more elements called <sB> etc
But I do not know how to get all of the child elements with <sA>, <sB>, etc.
How about this:
$xmlstr = LoadTheXMLFromSomewhere();
$xml = new simplexml_load_string($xmlstr);
$result = $xml->xpath('//sA');
foreach ($result as $node){
//do something with node
}
PHP does have a nice class to access XML, which is called SimpleXml for a reason, consider heavily using that if your code is going to access only a part of the XML (aka query the xml). Also, consider doing queries using XPath, which is the best way to do it
Notice that I did the example with sA nodes only, but you can configure your code for other node types really easily.
Hope I can help!
you should look into simplexml_load_string() as I'm pretty sure it would make your life a lot easier. It returns a StdObject that you can use like so:
$xml = simplexml_load_string(<your huge xml string>);
foreach ($xml->hpt_mobile->CATALOG->sA as $value){
// do things with sA children
}
$xml = new DOMDocument();
$xml->load('path_to_xml');
$htp = $xml->getElementsByTagName('hpt_mobile')[0];
$catalog = $htp->getElementsByTagName('CATALOG')[0]
$nodes = $catalog->getElementsByTagName('sA')->childNodes;

Categories