XPath - 选择不属于列表的空元素

时间:2012-05-30 14:25:46

标签: php xpath domxpath

$list = array('br', 'hr', 'link', 'meta', 'title');

使用DOMXpath,如何选择空的节点并且它们的tagName不在$ list中? (我想在textContent中添加一个空格,以便它们不会自动关闭)

5 个答案:

答案 0 :(得分:3)

你没有给我们任何XML使用,这不是很好,但是你去了:

$xml = <<<XML
<div>
   <a>
   </a>
   <p>some text</p>
   <p></p>
   <span>no text
      <hr/>
      <ul></ul>
   </span>
   <br/>
</div>
XML;

$dom = new DOMDocument;
$dom->loadXML($xml);
$xpath = new DOMXPath($dom);
$list = array('br', 'hr', 'link', 'meta', 'title');
$expr = array();
foreach ($list as $l) {
   $expr[] = "not(self::$l)";
}
$expr = implode(' and ', $expr);

foreach ($xpath->query("//*[$expr and not(normalize-space())]") as $elem) {
   echo "$elem->nodeName\n";
}

此输出

a
p
ul

正如所料。现在你有了节点 - 由你来添加空间。 IMO可以更轻松地使用not(normalize-space()),然后查看nodeName是否不在您的列表中,但是您要求使用XPath表达式,这就是您所拥有的。

请注意,使用normalize-space()是因为纯空格仍可能导致节点自动关闭。如果这不是问题,您可以改为使用node()

答案 1 :(得分:2)

这是一个单行的XPath表达式,可以选择想要的节点:

//*[not(node()[not(self::text())]) 
  and not(normalize-space) 
  and contains('|br|hr|link|meta|title|', concat('|', name(), '|'))
   ]

这将选择XML文档中仅包含文本节点子节点(如果有的话)且其规范化的所有元素(删除所有前导和尾随空白字符以及所有中间相邻空白字符替换为单个space)字符串值是空字符串,其名称是brhrmetatitle之一。

基于XSLT的验证

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>


  <xsl:template match="/">
   <xsl:copy-of select=
   "//*[not(node()[not(self::text())])
      and not(normalize-space)
      and contains('|br|hr|link|meta|title|', concat('|', name(), '|'))
       ]
   "/>
  </xsl:template>
</xsl:stylesheet>

对以下XML文档应用此转换时:

<html lang='en'>
    <head>
        <meta charset='utf-8'/>
        <title></title>
        <link rel='stylesheet' href='/assets/index.css'/>
    </head>
    <body>
        <div>
            <header>
                <h1></h1>
            </header>
            <section>
                <article></article>
                <aside></aside>
            </section>
            <br />
            <footer>
                <small>
                 Copyright &#169;
                    <span></span>
                </small>
            </footer>
        </div>
        <script src='//code.jquery.com/jquery-latest.min.js'></script>
        <script src='/assets/index.js'></script>
    </body>
</html>

评估XPath表达式并将(正确)选定的节点复制到输出中:

<meta charset="utf-8"/>
<title/>
<link rel="stylesheet" href="/assets/index.css"/>
<br/>

答案 2 :(得分:1)

$doc = new DOMDocument();
$doc->loadHTMLFile($file);
$xpath = new DOMXpath($doc);

$list = array('br', 'hr', 'link', 'meta', 'title');
$empty_items = $xpath->query("//*[not(text())]");
foreach($empty_items as $key=>$element){
    if(is_object($element) &&
       get_class($element) == 'DOMElement' &&
       in_array($element->nodeName,$list)){
        unset($empty_items[$key]);
    }
}

注意:我没有测试过。它可能有拼写错误或错误的对象属性。

答案 3 :(得分:1)

Xpath引擎无权访问PHP变量。您必须将列表引用为有效的Xpath表达式,或者必须在PHP中过滤dom节点。 PHP手册介绍了如何实现过滤器:http://www.php.net/manual/en/book.filter.php

答案 4 :(得分:1)

我使用类似的东西来完成类似的任务:

<?php
$xml = <<<XML
<html lang='en'>
  <head>
    <meta charset='utf-8'/>
    <title></title>
    <link rel='stylesheet' href='/assets/index.css'/>
  </head>
  <body>
    <div>
      <header>
        <h1></h1>
      </header>
      <section>
        <article></article>
        <aside></aside>
      </section>
      <footer>
        <small>
          Copyright &#169;
          <span></span>
        </small>
      </footer>
    </div>
    <script src='//code.jquery.com/jquery-latest.min.js'></script>
    <script src='/assets/index.js'></script>
  </body>
</html>
XML;
$dom = new DOMDocument;
$dom->loadXML($xml);
$xpath = new DOMXPath($dom);
$null = array( 'br','hr','meta','link','base','link','meta','img'
             , 'embed','param','area','col','input' );
array_walk($null, function(&$v){$v = "not(self::{$v})";});
array_unshift($null, 'not(normalize-space())');
$null = implode(' and ', $null);
$node = $xpath->query("//*[{$null}]");

$collapsed = htmlspecialchars($dom->saveXML($dom->documentElement));
foreach ($node as $n) $n->appendChild($dom->createTextNode(''));
$separated = htmlspecialchars($dom->saveXML($dom->documentElement));

echo '<pre>', $collapsed, '<hr/>', $separated, '</pre>';
?>