2

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

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

4

5 回答 5

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()改用。

于 2012-05-30T14:40:56.417 回答
3

这是一个选择所需节点的单行 XPath 表达式:

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

这将选择 XML 文档中只有一个文本节点子节点(如果有的话)并且其规范化(所有前导和尾随空白字符被删除并且所有中间相邻空白字符被单个空格替换)字符串的任何元素value 是空字符串,其名称是br、或之一。hrmetatitle

基于 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/>
于 2012-05-31T02:37:22.810 回答
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]);
    }
}

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

于 2012-05-30T14:38:55.157 回答
1

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

于 2012-05-30T14:46:13.740 回答
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>';
?>
于 2012-05-31T00:04:50.147 回答