Xpath最深节点,其字符串内容长于给定长度

时间:2010-12-20 19:53:18

标签: xml xhtml xpath

如何使用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选择最深的匹配节点(即:&lt; div id="desc">)?

奖励积分,如何将约束应用于空间规范化内容长度?

2 个答案:

答案 0 :(得分:3)

这不能表示为单个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::*)
      ]

答案 1 :(得分:2)

正如Dimitre指出的那样,在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>