I have the following XML:
<root>
<level name="level1">
<!-- More children <level> -->
</level>
<level name="level2">
<!-- Some more children <level> -->
</level>
</root>
How can I extract a <level>
directly under <root>
so that I can run an XPath query such as $xml->xpath('//some-query')
relative to the extracted <level>
?
DOMXpath::evaluate()
allows you to fetch node lists and scalar values from a DOM.
So you can fetch a value directly using an Xpath expression:
$document = new DOMDocument();
$document->loadXml($xml);
$xpath = new DOMXpath($document);
var_dump(
$xpath->evaluate('string(/root/level[@name="level2"]/@name)')
);
Output:
string(6) "level2"
All level
element nodes in root
:
/root/level
That have a specific name attribute:
/root/level[@name="level2"]
The value you like to fetch ( name
attribute for validation):
/root/level[@name="level2"]/@name
Cast into a string, if node was found the result will be an empty string:
string(/root/level[@name="level2"]/@name)
If you need to execute several expression for the node it might be better to fetch it separately and use foreach()
. The second argument for DOMXpath::evaluate()
is the context node.
foreach ($xpath->evaluate('/root/level[@name="level2"]') as $level) {
var_dump(
$xpath->evaluate('string(@name)', $level)
);
}
If you need to handle that no node was found you can check the DOMNodeList::$length
property.
$levels = $xpath->evaluate('/root/level[@name="level2"]');
if ($levels->length > 0) {
$level = $levels->item(0);
var_dump(
$xpath->evaluate('string(@name)', $level)
);
} else {
// no level found
}
You can validate that here are elements before with a count()
expression, too.
var_dump(
$xpath->evaluate('count(/root/level[@name="level2"])')
);
Output:
float(1)
It is possible to make that a condition in Xpath and return the boolean value.
var_dump(
$xpath->evaluate('count(/root/level[@name="level2"]) > 0')
);
Output:
bool(true)
DOMXPath::query 's second parameter is the context node. Just pass the DOMNode instance you have previously "found" and your query runs "relative" to that node. Eg
<?php
$doc = new DOMDocument;
$doc->loadxml( data() );
$xpath = new DOMXPath($doc);
$nset = $xpath->query('/root/level[@name="level1"]');
if ( $nset->length < 1 ) {
die('....no such element');
}
else {
$elLevel = $nset->item(0);
foreach( $xpath->query('c', $elLevel) as $elC) {
echo $elC->nodeValue, "\r\n";
}
}
function data() {
return <<< eox
<root>
<level name="level1">
<c>C1</c>
<a>A</a>
<c>C2</c>
<b>B</b>
<c>C3</c>
</level>
<level name="level2">
<!-- Some more children <level> -->
</level>
</root>
eox;
}
But unless you have to perform multiple separate (possible complex) subsequent queries, this is most likely not necessary
<?php
$doc = new DOMDocument;
$doc->loadxml( data() );
$xpath = new DOMXPath($doc);
foreach( $xpath->query('/root/level[@name="level1"]/c') as $c ) {
echo $c->nodeValue, "\r\n";
}
function data() {
return <<< eox
<root>
<level name="level1">
<c>C1</c>
<a>A</a>
<c>C2</c>
<b>B</b>
<c>C3</c>
</level>
<level name="level2">
<c>Ahh</c>
<a>ouch</a>
<c>no</c>
<b>wrxl</b>
</level>
</root>
eox;
}
has the same output using just one query.
This should work:
$dom = new DOMDocument;
$dom->loadXML($xml);
$levels = $dom->getElementsByTagName('level');
foreach ($levels as $level) {
$levelname = $level->getAttribute('name');
if ($levelname == 'level1') {
//do stuff
}
}
I personally prefer the DOMNodeList class for parsing XML.
Using querypath for parsing XML/HTML makes this all super easy.
$qp = qp($xml) ;
$levels = $qp->find('root')->eq(0)->find('level') ;
foreach($levels as $level ){
//do whatever you want with it , get its xpath , html, attributes etc.
$level->xpath() ; //
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.