2012-05-30 4 views
2

$list = array('br', 'hr', 'link', 'meta', 'title');XPath - выберите пустые элементы, которые не являются частью списка

Использование DOMXPath, как я могу выбрать узлы, которые являются пустыми и их Тэг не в $ списке? (Я хочу, чтобы добавить пробел в их textContent, чтобы они не закрываются автоматически)

ответ

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 

Как и следовало ожидать. Теперь у вас есть узлы - вам нужно добавить пространство. ИМО было бы проще просто использовать not(normalize-space()), а затем посмотреть, нет ли в вашем списке nodeName, но вы попросили выражение XPath, так вот что вы получили.

Обратите внимание, что normalize-space() используется, поскольку чистые пробелы могут все еще привести к автоматическому закрытию узла. Если это не проблема, вместо этого вы можете использовать node().

+0

спасибо, что работает: D – Alex

+0

Кстати, ваш список пустых элементов модели контента в HTML является неполным. Я также не уверен, что произойдет, если вы используете 'loadHTML' для' DOMDocument', но у вас могут быть лучшие результаты. –

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]); 
    } 
} 

Примечание: я не проверял. Он может иметь опечатки или неправильные свойства объекта.

1

У Xpath-движка нет доступа к переменным PHP. Вы должны процитировать список как допустимое выражение Xpath или вам нужно отфильтровать dom-узлы в PHP. PHP руководство объясняет, как реализовать фильтры: http://www.php.net/manual/en/book.filter.php

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>'; 
?> 
+0

добавление не требуется. вы можете просто использовать '$ n-> nodeValue = ''' – Alex

2

Вот одно выражение XPath один вкладыш, который выбирает разыскиваемых узлы:

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

Это выбирает любой элемент в документе XML, который имеет только текстовый дочерний узел (если вообще) и чьи нормализованные (все начальные и конечные символы йота-пространство удалены и все взаимосвязаны опознавание смежных символов пробела, замененных одним пробелом), строковое значение - это пустая строка, имя которой является одним из br, hr, meta или title.

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/> 
Смежные вопросы