XML attributes and PHP [duplicate] - php

This question already has answers here:
Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?
(2 answers)
Closed 1 year ago.
I have this as xml:
<root xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
<event:event>
<event:sessionKey></event:sessionKey>
<event:sessionName>Learn QB in Minutes</event:sessionName>
<event:sessionType>9</event:sessionType>
<event:hostWebExID></event:hostWebExID>
<event:startDate>02/12/2009</event:startDate>
<event:endDate>02/12/2009</event:endDate>
<event:timeZoneID>11</event:timeZoneID>
<event:duration>30</event:duration>
<event:description></event:description>
<event:status>NOT_INPROGRESS</event:status>
<event:panelists></event:panelists>
<event:listStatus>PUBLIC</event:listStatus>
</event:event>
...
</root>
How can I loop through all of the event:event nodes and display, for example, all of the event:SessionKey's?
This does not work:
$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
foreach($xml->xpath('//e:event') as $event) {
var_export($event->xpath('//e:sessionKey'));
}

it does work without registerXPathNamespace and the full namespace prefix in the xpath queries:
$xml = new SimpleXMLElement($r);
foreach($xml->xpath('//event:event') as $event) {
var_export($event->xpath('event:sessionKey'));
}

You have to register the namespace for each simpleXMLElement object you use.
$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
foreach($xml->xpath('//e:event') as $event) {
$event->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
var_export($event->xpath('//e:sessionKey'));
}
The namespace should also be declared somewhere in the xml file.
<event:event xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
...
The method ax described works too. You can skip the registerXPathNamespace if you know the xml file will always use the same prefix.

Having worked a lot with simplexml, this is how I do it.
The magic trick if you already have an element and just want to get its different namespaced children, say for a structure like this:
<entry>
<title type="text">My test entry</title>
<gd:when startTime="2017-02-26T02:00:00Z" endTime="2017-02-26T03:00:00Z"/>
<gc:notes type="string">A type</gc:notes>
</entry>
Is to send TRUE as the second parameter to the children function:
$title = (string) $entry->title;
$gd = $entry->children('gd', TRUE);
$attrs = $gd->when->attributes();
$startTime = (string) $attrs->startTime;
$gc = $entry->children('gc', TRUE);
$notes = (string) $gc->notes();

here alternative that worked for me.
$xml = simplexml_load_string($r);
$ns = $xml->getNamespaces(true);
foreach ($xml->children($ns['event'])->event as $skey) {
$sessionKey = $skey->children($ns['event'])->sessionKey;
echo $sessionKey;
}

Another approach is to use SimpleXML for parsing and DOMDocument for manipulation/access, which bypasses namespacing issues altogether:
$xml = new SimpleXMLElement($r);
$xml = dom_import_simplexml($xml);
$nodelist= $xml->getElementsByTagName('event');
for($i = 0; $i < $nodelist->length; $i++) {
$sessions = $nodelist->item($i)->getElementsByTagName('sessionKey');
echo $sessions->item(0)->nodeValue;
}

Using registerXPathNamespace and then calling xpath didn't actually work for me.
I had to go with the solution provided in this great post : http://blog.preinheimer.com/index.php?/archives/172-SimpleXML,-Namespaces-Hair-loss.html
So in your case, this :
echo $xml->children('http://www.webex.com/schemas/2002/06/service/event')->sessionName;
Will output:
Learn QB in Minutes

Related

Parse YouTube RSS XML data <media:description> using PHP [duplicate]

This question already has answers here:
Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?
(2 answers)
Closed 1 year ago.
I have this as xml:
<root xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
<event:event>
<event:sessionKey></event:sessionKey>
<event:sessionName>Learn QB in Minutes</event:sessionName>
<event:sessionType>9</event:sessionType>
<event:hostWebExID></event:hostWebExID>
<event:startDate>02/12/2009</event:startDate>
<event:endDate>02/12/2009</event:endDate>
<event:timeZoneID>11</event:timeZoneID>
<event:duration>30</event:duration>
<event:description></event:description>
<event:status>NOT_INPROGRESS</event:status>
<event:panelists></event:panelists>
<event:listStatus>PUBLIC</event:listStatus>
</event:event>
...
</root>
How can I loop through all of the event:event nodes and display, for example, all of the event:SessionKey's?
This does not work:
$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
foreach($xml->xpath('//e:event') as $event) {
var_export($event->xpath('//e:sessionKey'));
}
it does work without registerXPathNamespace and the full namespace prefix in the xpath queries:
$xml = new SimpleXMLElement($r);
foreach($xml->xpath('//event:event') as $event) {
var_export($event->xpath('event:sessionKey'));
}
You have to register the namespace for each simpleXMLElement object you use.
$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
foreach($xml->xpath('//e:event') as $event) {
$event->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
var_export($event->xpath('//e:sessionKey'));
}
The namespace should also be declared somewhere in the xml file.
<event:event xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
...
The method ax described works too. You can skip the registerXPathNamespace if you know the xml file will always use the same prefix.
Having worked a lot with simplexml, this is how I do it.
The magic trick if you already have an element and just want to get its different namespaced children, say for a structure like this:
<entry>
<title type="text">My test entry</title>
<gd:when startTime="2017-02-26T02:00:00Z" endTime="2017-02-26T03:00:00Z"/>
<gc:notes type="string">A type</gc:notes>
</entry>
Is to send TRUE as the second parameter to the children function:
$title = (string) $entry->title;
$gd = $entry->children('gd', TRUE);
$attrs = $gd->when->attributes();
$startTime = (string) $attrs->startTime;
$gc = $entry->children('gc', TRUE);
$notes = (string) $gc->notes();
here alternative that worked for me.
$xml = simplexml_load_string($r);
$ns = $xml->getNamespaces(true);
foreach ($xml->children($ns['event'])->event as $skey) {
$sessionKey = $skey->children($ns['event'])->sessionKey;
echo $sessionKey;
}
Another approach is to use SimpleXML for parsing and DOMDocument for manipulation/access, which bypasses namespacing issues altogether:
$xml = new SimpleXMLElement($r);
$xml = dom_import_simplexml($xml);
$nodelist= $xml->getElementsByTagName('event');
for($i = 0; $i < $nodelist->length; $i++) {
$sessions = $nodelist->item($i)->getElementsByTagName('sessionKey');
echo $sessions->item(0)->nodeValue;
}
Using registerXPathNamespace and then calling xpath didn't actually work for me.
I had to go with the solution provided in this great post : http://blog.preinheimer.com/index.php?/archives/172-SimpleXML,-Namespaces-Hair-loss.html
So in your case, this :
echo $xml->children('http://www.webex.com/schemas/2002/06/service/event')->sessionName;
Will output:
Learn QB in Minutes

How to remove XML elements and all children?

I need to read an XML file and delete all the elements named <images> and all the children associated. I have found similar old questions that did not work. What am I doing wrong? Is there a better method?
XML:
<?xml version='1.0' encoding='UTF-8'?>
<settings>
<background_color>#000000</background_color>
<show_context_menu>yes</show_context_menu>
<image>
<thumb_path>210x245.png</thumb_path>
<big_image_path>620x930.png</big_image_path>
</image>
<image>
<thumb_path>200x295.png</thumb_path>
<big_image_path>643x950.png</big_image_path>
</image>
</settings>
PHP:
$dom = new DOMDocument();
$dom->load('test.xml');
$thedocument = $dom->documentElement;
$elements = $thedocument->getElementsByTagName('image');
foreach ($elements as $node) {
$node->parentNode->removeChild($node);
}
$save = $dom->saveXML();
file_put_contents('test.xml', $save)
I figured it out after a good night of sleep. It was quite simple actually.
$xml = simplexml_load_file('test.xml');
unset($xml->image);
$xml_file = $xml->asXML();
$xmlFile = 'test.xml';
$xmlHandle = fopen($xmlFile, 'w');
fwrite($xmlHandle, $xml_file);
fclose($xmlHandle);
Edit: You probably want to make it save directly:
$file = 'test.xml';
$xml = simplexml_load_file($file);
unset($xml->image);
$success = $xml->asXML($file);
See SimpleXMLElement::asXML()Docs.
In the PHP Manual page (where you should always go 1st :-) one awesome contributor points out that:
You can't remove DOMNodes from a DOMNodeList as you're iterating over them in a foreach loop.
Then goes on to offer a potential solution. Try something like this instead:
<?php
$domNodeList = $domDocument->getElementsByTagname('p');
$domElemsToRemove = array();
foreach ( $domNodeList as $domElement ) {
// ...do stuff with $domElement...
$domElemsToRemove[] = $domElement;
}
foreach( $domElemsToRemove as $domElement ){
$domElement->parentNode->removeChild($domElement);
}
?>
First of all, your XML is broken, see <thumb>...</thumb_path>and next line as well -> fix it!
Then, real simple in 3 lines of code:
$xml = simplexml_load_string($x); // $x holds your xml
$count = $xml->image->count()-1;
for ($i = $count;$i >= 0;$i--) unset($xml->image[$i]);
See live demo # http://codepad.viper-7.com/HkGy5o

PHP DOM: How to move element into default namespace?

What I tried and what doesn't work:
Input:
$d = new DOMDocument();
$d->formatOutput = true;
// Out of my control:
$someEl = $d->createElementNS('http://example.com/a', 'a:some');
// Under my control:
$envelopeEl = $d->createElementNS('http://example.com/default',
'envelope');
$d->appendChild($envelopeEl);
$envelopeEl->appendChild($someEl);
echo $d->saveXML();
$someEl->prefix = null;
echo $d->saveXML();
Output is invalid XML after substitution:
<?xml version="1.0"?>
<envelope xmlns="http://example.com/default">
<a:some xmlns:a="http://example.com/a"/>
</envelope>
<?xml version="1.0"?>
<envelope xmlns="http://example.com/default">
<:some xmlns:a="http://example.com/a" xmlns:="http://example.com/a"/>
</envelope>
Note that <a:some> may have children. One solution would be
to create a new <some>, and copy all children from <a:some> to <some>. Is
that the way to go?
This is really an interesting question. My first intention was to clone the <a:some> node, remove the xmlns:a attribute, remove the <a:some> and insert the clone - <a>. But this will not work, as PHP does not allow to remove the xmlns:a attribute like any regular attribute.
After some struggling with DOM methods of PHP I started to google the problem. I found this comment in the PHP documentation on this. The user suggest to write a function that clones the node manually without it's namespace:
<?php
/**
* This function is based on a comment to the PHP documentation.
* See: http://www.php.net/manual/de/domnode.clonenode.php#90559
*/
function cloneNode($node, $doc){
$unprefixedName = preg_replace('/.*:/', '', $node->nodeName);
$nd = $doc->createElement($unprefixedName);
foreach ($node->attributes as $value)
$nd->setAttribute($value->nodeName, $value->value);
if (!$node->childNodes)
return $nd;
foreach($node->childNodes as $child) {
if($child->nodeName == "#text")
$nd->appendChild($doc->createTextNode($child->nodeValue));
else
$nd->appendChild(cloneNode($child, $doc));
}
return $nd;
}
Using it would lead to a code like this:
$xml = '<?xml version="1.0"?>
<envelope xmlns="http://example.com/default">
<a:some xmlns:a="http://example.com/a"/>
</envelope>';
$doc = new DOMDocument();
$doc->loadXML($xml);
$elements = $doc->getElementsByTagNameNS('http://example.com/a', 'some');
$original = $elements->item(0);
$clone = cloneNode($original, $doc);
$doc->documentElement->replaceChild($clone, $original);
$doc->formatOutput = TRUE;
echo $doc->saveXML();

Parsing XML with multiple namespaces in PHP

I have XML in the following form that I want to parse with PHP (I can't change the format of the XML). Neither SimpleXML nor DOM seem to handle the different namespaces - can anyone give me sample code? The code below gives no results.
<atom:feed>
<atom:entry>
<atom:id />
<otherns:othervalue />
</atom:entry>
<atom:entry>
<atom:id />
<otherns:othervalue />
</atom:entry>
</atom:feed>
$doc = new DOMDocument();
$doc->load($url);
$entries = $doc->getElementsByTagName("atom:entry");
foreach($entries as $entry) {
$id = $entry->getElementsByTagName("atom:id");
echo $id;
$othervalue = $entry->getElementsByTagName("otherns:othervalue");
echo $othervalue;
}
I just want to post with an answer to this awful question. Sorry.
Namespaces are irrelavent with DOM - I just wasn't getting the nodeValue from the Element.
$doc = new DOMDocument();
$doc->load($url);
$feed = $doc->getElementsByTagName("entry");
foreach($feed as $entry) {
$id = $entry->getElementsByTagName("id")->item(0)->nodeValue;
echo $id;
$id = $entry->getElementsByTagName("othervalue")->item(0)->nodeValue;
echo $othervalue;
}
You need to register your name spaces. Otherwise simplexml will ignore them.
This bit of code I got from the PHP manual and I used in my own project
$xmlsimple = simplexml_load_string('YOUR XML');
$namespaces = $xmlsimple->getNamespaces(true);
$extensions = array_keys($namespaces);
foreach ($extensions as $extension )
{
$xmlsimple->registerXPathNamespace($extension,$namespaces[$extension]);
}
After that you use xpath on $xmlsimple

Parse XML with Namespace using SimpleXML [duplicate]

This question already has answers here:
Reference - How do I handle Namespaces (Tags and Attributes with a Colon in their Name) in SimpleXML?
(2 answers)
Closed 1 year ago.
I have this as xml:
<root xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
<event:event>
<event:sessionKey></event:sessionKey>
<event:sessionName>Learn QB in Minutes</event:sessionName>
<event:sessionType>9</event:sessionType>
<event:hostWebExID></event:hostWebExID>
<event:startDate>02/12/2009</event:startDate>
<event:endDate>02/12/2009</event:endDate>
<event:timeZoneID>11</event:timeZoneID>
<event:duration>30</event:duration>
<event:description></event:description>
<event:status>NOT_INPROGRESS</event:status>
<event:panelists></event:panelists>
<event:listStatus>PUBLIC</event:listStatus>
</event:event>
...
</root>
How can I loop through all of the event:event nodes and display, for example, all of the event:SessionKey's?
This does not work:
$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
foreach($xml->xpath('//e:event') as $event) {
var_export($event->xpath('//e:sessionKey'));
}
it does work without registerXPathNamespace and the full namespace prefix in the xpath queries:
$xml = new SimpleXMLElement($r);
foreach($xml->xpath('//event:event') as $event) {
var_export($event->xpath('event:sessionKey'));
}
You have to register the namespace for each simpleXMLElement object you use.
$xml = new SimpleXMLElement($r);
$xml->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
foreach($xml->xpath('//e:event') as $event) {
$event->registerXPathNamespace('e', 'http://www.webex.com/schemas/2002/06/service/event');
var_export($event->xpath('//e:sessionKey'));
}
The namespace should also be declared somewhere in the xml file.
<event:event xmlns:event="http://www.webex.com/schemas/2002/06/service/event">
...
The method ax described works too. You can skip the registerXPathNamespace if you know the xml file will always use the same prefix.
Having worked a lot with simplexml, this is how I do it.
The magic trick if you already have an element and just want to get its different namespaced children, say for a structure like this:
<entry>
<title type="text">My test entry</title>
<gd:when startTime="2017-02-26T02:00:00Z" endTime="2017-02-26T03:00:00Z"/>
<gc:notes type="string">A type</gc:notes>
</entry>
Is to send TRUE as the second parameter to the children function:
$title = (string) $entry->title;
$gd = $entry->children('gd', TRUE);
$attrs = $gd->when->attributes();
$startTime = (string) $attrs->startTime;
$gc = $entry->children('gc', TRUE);
$notes = (string) $gc->notes();
here alternative that worked for me.
$xml = simplexml_load_string($r);
$ns = $xml->getNamespaces(true);
foreach ($xml->children($ns['event'])->event as $skey) {
$sessionKey = $skey->children($ns['event'])->sessionKey;
echo $sessionKey;
}
Another approach is to use SimpleXML for parsing and DOMDocument for manipulation/access, which bypasses namespacing issues altogether:
$xml = new SimpleXMLElement($r);
$xml = dom_import_simplexml($xml);
$nodelist= $xml->getElementsByTagName('event');
for($i = 0; $i < $nodelist->length; $i++) {
$sessions = $nodelist->item($i)->getElementsByTagName('sessionKey');
echo $sessions->item(0)->nodeValue;
}
Using registerXPathNamespace and then calling xpath didn't actually work for me.
I had to go with the solution provided in this great post : http://blog.preinheimer.com/index.php?/archives/172-SimpleXML,-Namespaces-Hair-loss.html
So in your case, this :
echo $xml->children('http://www.webex.com/schemas/2002/06/service/event')->sessionName;
Will output:
Learn QB in Minutes

Categories