【问题标题】:How to select all nodes of a DOMDocument with a single DOMXpath expression?如何使用单个 DOMXpath 表达式选择 DOMDocument 的所有节点?
【发布时间】:2022-02-13 00:09:15
【问题描述】:

选择文档所有节点的 xpath 表达式是什么?

鉴于此示例 XML:

<div class="header"/>

我包含三个节点:&lt;div&gt;(元素)、class=(属性)和"header"(文本)。

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

我试过//node():

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

它只返回所有元素节点(我假设是因为//)。有没有办法在属性值中添加其他节点,如属性和文本节点?


补充例子:

我可以使用DOMDocument API 获取每个节点,例如获取属性值的文本节点:

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

这给出了:

#text

如何用一个 xpath 表达式获得所有节点的超集,尤其包括示例classattribute-node child text-node?​​p>

【问题讨论】:

    标签: php xpath domdocument


    【解决方案1】:

    使用:

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

    这选择任何节点(类型为文档节点/、元素节点、文本节点、处理指令节点和注释节点)以及任何属性节点和任何命名空间节点——即所有节点因为没有其他类型的节点。

    如何访问获得的包含所选节点的 XmlNodeList 取决于您正在使用的特定 XPath 引擎的 API -- 阅读并使用您的文档。

    基于 XSLT 的示例:

    <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>
    

    当此 XSLT 转换应用于任何 XML 文档时,它会使用上述 XPath 表达式选择所有节点(转换有意排除任何纯空白文本节点)并输出(按文档顺序)类型、名称和所选节点的字符串值。

    例如,应用于此 XML 文档时:

    <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>
    

    结果是:

       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
    

    【讨论】:

      【解决方案2】:

      您的示例实际上只包含两个节点:一个元素 (div) 和一个属性 (class="header")。因此,“header”是属性的值,而不是单独的节点。

      文本节点确实存在,但它们用于元素之间的文本。例如,&lt;title&gt;Alice in wonderland&lt;/title&gt; 中有两个节点:一个元素 (title) 和一个文本节点 (Alice in wonderland)。

      因此,在这种情况下,您可以做的最好的事情是//*|//@*。

      编辑,在您更新问题后。

      文本节点的存在是由于特定于 php 的实现,它不是W3C standard 的一部分。无论实现如何,XPath 只考虑 2 个节点。

      话虽如此,您可以使用一些XPath functions 来获得您想要的东西。函数name() 返回节点的名称,函数string() 返回字符串值。也许您可以使用这些来获取字符串(而不是节点)。

      【讨论】:

        【解决方案3】:

        你试过//*|//@*|//text()之类的吗?

        【讨论】:

        • 我现在对//*|//@* 进行了处理,它将选择所有元素和属性,但不选择属性值。我省略了//text(),因为它在示例 XML 中没有任何区别。所以你的建议可能是朝着正确方向迈出的一步。
        • 我认为你不能自己获取属性值。
        【解决方案4】:
        $doc = new DOMDocument;
        
        // We don't want to bother with white spaces
        $doc->preserveWhiteSpace = false;
        
        $doc->load('book.xml');
        
        $xpath = new DOMXPath($doc);
        
        // We start from the root element
        $query = '//book/chapter/para/informaltable/tgroup/tbody/row/entry[. = 
        "en"]';
        
        $entries = $xpath->query($query);
        
        foreach ($entries as $entry) {
             echo "Found {$entry->previousSibling->previousSibling->nodeValue}," .
                 " by {$entry->previousSibling->nodeValue}\n";
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-10-03
          • 1970-01-01
          • 2012-02-18
          • 1970-01-01
          • 1970-01-01
          • 2019-01-19
          • 1970-01-01
          • 2019-01-17
          相关资源
          最近更新 更多