Xpath самый глубокий узел, чье содержимое строки длиннее заданной длины - PullRequest
1 голос
/ 20 декабря 2010

Как использовать XPath для поиска самого глубокого узла, соответствующего ограничению длины содержимого строки.

Учитывая фрагмент XHTML (или XML), который выглядит следующим образом:

<html>
    <body>
        <div id="page">
             <div id="desc">
                  This wool sweater has the following features:
                  <ul>
                       <li>4 buttons</li>
                       <li>Merino Wool</li>
                  </ul>
             </div>
        </div>
        ...
     </body>
</html>

Выражение XPath типа

//*[string-length() > 50]

Будет соответствовать <html>, <body>, <div id="page"> and <div id="desc">. Как заставить XPath выбрать самый глубокий соответствующий узел (т. Е. <<code>div id="desc">)?

Бонусные баллы, как применить ограничение к нормализованной по длине области контента?

Ответы [ 2 ]

3 голосов
/ 20 декабря 2010

Это не может быть выражено как одно выражение XPath 1.0 (без использования переменных)

Одно выражение XPath 2.0 :

//*[string-length(.) > 50]
      [count(ancestor::*) >= //*[string-length(.) > 50]/count(ancestor::*)]

XPathВыражение 1.0 с использованием переменной :

//*[string-length() > 50]
         [not(//*[string-length() > 50 
        and count(ancestor::*) > $vNumAncestrors])
         ]

, где переменная vNumAncestrors содержит значение count(ancestor::*) для узла контекста.

Последнее выражение может быть реализовано вязык хостинга, такой как XSLT 1.0 или DOM.

Вот одна реализация XSLT 1.0 :

<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:variable name="vLongTextElements"
   select="//*[string-length()>50]"/>

  <xsl:for-each select="$vLongTextElements">
   <xsl:variable name="vNumAncestrors"
        select="count(ancestor::*)"/>

    <xsl:copy-of select=
    "(.)[not(//*[string-length() > 50
            and count(ancestor::*) > $vNumAncestrors])
         ]
    "/>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

, когда это преобразование применяется к предоставленному документу XML :

<html>
    <body>
        <div id="page">
            <div id="desc">                                This wool sweater has the following features:                                
                <ul>
                    <li>4 buttons</li>
                    <li>Merino Wool</li>
                </ul>
            </div>
        </div>                      ...                   
    </body>
</html>

желаемый, правильный результат получен :

<div id="desc">                                This wool sweater has the following features:                                
                <ul>

      <li>4 buttons</li>

      <li>Merino Wool</li>

   </ul>

</div>

Бонусные баллы, как применить ограничение к нормированной по пространству длине контента?

Очень просто реализовать поверх последнего решения :

<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:variable name="vLongTextElements"
   select="//*[string-length(normalize-space())>50]"/>

  <xsl:for-each select="$vLongTextElements">
   <xsl:variable name="vNumAncestrors"
        select="count(ancestor::*)"/>

    <xsl:copy-of select=
    "(.)[not(//*[string-length(normalize-space()) > 50
            and count(ancestor::*) > $vNumAncestrors])
         ]
    "/>
  </xsl:for-each>
 </xsl:template>
</xsl:stylesheet>

И первоначальное выражение XPath 2.0 теперь изменено на это :

//*[string-length(normalize-space(.)) > 50]
      [count(ancestor::*) 
     >= 
      //*[string-length(normalize-space(.)) > 50]/count(ancestor::*)
      ]
2 голосов
/ 21 декабря 2010

Как указал Димитр, проблема для решения этой проблемы в XPath 1.0 заключается в том, что максимальное выражение работает только для не вычисляемых значений:

$node-set[not($node-set/node-or-attribute > node-or-attribute)]

Именно поэтому в XSLT 1.0 вы будете использовать "стандартный" максимумконструкция:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="/">
        <xsl:for-each select="//*[string-length(normalize-space())>50]">
            <xsl:sort select="count(ancestor::*)" 
                      data-type="number" order="descending"/>
            <xsl:if test="position()=1">
                <xsl:copy-of select="."/>
            </xsl:if>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Выход:

<div id="desc">                   This wool sweater has the following features:                   
                <ul>
<li>4 buttons</li>
<li>Merino Wool</li>
</ul>
</div>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...