How to select all DOMDocument nodes with one DOMXpath expression?

What is an xpath expression to select all nodes of a document?

In this XML example:

<div class="header"/> 

I have three nodes: <div> (element), class= (attribute) and "header" (text).

 $doc = new DOMDocument; $doc->loadXml('<div class="header"/>'); $xpath = new DOMXPath($doc); 

I tried with //node() :

 $xpath->query('//node()'); 

which returns only all the nodes of the element (I suppose due to // ). Is there a way to add other attributes and attributes to attribute values?


Additional example:

I can get each node using the DOMDocument API, for example. to get the text node attribute value:

 $doc = new DOMDocument; $doc->loadXml('<div class="header"/>'); $class = $doc->documentElement->getAttributeNode('class'); echo $class->childNodes->item(0)->nodeName; 

What gives:

 #text 

How to get a superset of all nodes with a single xpath expression, especially including an example class attribute-node child text-node?

+4
source share
4 answers

Using

 //node() | //@* | //namespace::* 

this selects any node (document type node / , element node, node text, node processing instruction and comment node) and any node attribute and any node namespace are all nodes because there are no other types of nodes.

How you access the resulting XmlNodeList containing the selected nodes depends on the API of the particular XPath engine that you use — read and use your documentation.

XSLT Example :

 <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" indent="yes"/> <xsl:strip-space elements="*"/> <xsl:template match="/"> <xsl:for-each select= "//node() | //@* | //namespace::*"> Type: <xsl:text/> <xsl:choose> <xsl:when test="not(..)"> <xsl:text>document node </xsl:text> </xsl:when> <xsl:when test="self::*"> <xsl:text>element </xsl:text> </xsl:when> <xsl:when test="self::text()"> <xsl:text>text-node </xsl:text> </xsl:when> <xsl:when test="self::comment()"> <xsl:text>comment-node </xsl:text> </xsl:when> <xsl:when test="self::processing-instruction()"> <xsl:text>PI-node </xsl:text> </xsl:when> <xsl:when test="count(.|../@*) = count(../@*)"> <xsl:text>attribute-node </xsl:text> </xsl:when> <xsl:when test= "count(.|../namespace::*) = count(../namespace::*)"> <xsl:text>namespace-node </xsl:text> </xsl:when> </xsl:choose> <xsl:text>Name: "</xsl:text> <xsl:value-of select="name()"/>" <xsl:text/> <xsl:text>Value: </xsl:text> <xsl:value-of select="."/> </xsl:for-each> </xsl:template> </xsl:stylesheet> 

when this XSLT transformation is applied to any XML document, it selects all nodes using the above XPath expression (the conversion intentionally excludes any text nodes for space only) and displays (in document order) the type, name and string value of the selected nodes .

For example, when applying this XML document :

 <networkOfBridges xmlns:x="x"> <bridge id="1" otherside="A" /> <!-- A Comment --> <bridge id="2" oneside="A"/> <?PI Processing Instruction ?> <bridge id="3" oneside="A" otherside="A" /> </networkOfBridges> 

result :

  Type: element Name: "networkOfBridges" Value: Type: namespace-node Name: "xml" Value: http://www.w3.org/XML/1998/namespace Type: namespace-node Name: "x" Value: x Type: element Name: "bridge" Value: Type: namespace-node Name: "xml" Value: http://www.w3.org/XML/1998/namespace Type: namespace-node Name: "x" Value: x Type: attribute-node Name: "id" Value: 1 Type: attribute-node Name: "otherside" Value: A Type: comment-node Name: "" Value: A Comment Type: element Name: "bridge" Value: Type: namespace-node Name: "xml" Value: http://www.w3.org/XML/1998/namespace Type: namespace-node Name: "x" Value: x Type: attribute-node Name: "id" Value: 2 Type: attribute-node Name: "oneside" Value: A Type: PI-node Name: "PI" Value: Processing Instruction Type: element Name: "bridge" Value: Type: namespace-node Name: "xml" Value: http://www.w3.org/XML/1998/namespace Type: namespace-node Name: "x" Value: x Type: attribute-node Name: "id" Value: 3 Type: attribute-node Name: "oneside" Value: A Type: attribute-node Name: "otherside" Value: A 
+3
source

Your example actually contains only two nodes: an element ( div ) and an attribute ( class="header" ). So, "header" is an attribute value, not a separate node.

Text nodes exist, but they are used for text between elements. For example, in <title>Alice in wonderland</title> there are two nodes: the element ( title ) and the text node ( Alice in wonderland ).

Therefore, the best you can do in this case is //*|//@* .

EDIT after your update in question.

The existence of node text is due to a specific php implementation and is not part of the W3C standard . There are only 2 nodes that XPath counts regardless of implementation.

Having said that, you can use some XPath functions to get what you want. The name() function returns the name of the node, and the string() function returns a string value. Perhaps you could use them to take the strings as a result (instead of nodes).

+3
source

Have you tried something like //*|//@*|//text() ?

+1
source
 foreach ($xpath->query('//*[count(*) = 0]') as $node) { $path = array(); $val = $node->nodeValue; do { $path[] = $node->nodeName; } while ($node = $node->parentNode); $result[implode('/', array_reverse($path))] = $val; } 
-one
source

Source: https://habr.com/ru/post/1392307/


All Articles