1

我有以下标记,一个混合了 TR 和一个或两个子 TD 的表

  <table>
    <tr>
      <td>
         <p>XXX</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>XXX</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>XXX</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
    <tr>
      <td>
        <p>YYYYYY</p>
      </td>
      <td>
        <p>YYYYYY</p>
      </td>
    </tr>
  </table>

我正在尝试使用 XSLT 1.0 将其转换为

<AAA>
  XXX
  <BBB>YYYYY</BBB>
  <BBB>YYYYY</BBB>
  ...
</AAA>
<AAA>
  XXX
  <BBB>YYYYY</BBB>
  <BBB>YYYYY</BBB>
  ...
</AAA>
...

抽象出这可能是在 XSLT 中处理嵌套循环的最佳方法,XPath 表达式放入了什么???下面将选择当前 TR (XXX) 的所有 TR 兄弟姐妹,它们有两个 TD 孩子,但只到下一个 TR (XXX)。我根据它们有多少 TD 子节点来区分包含 XXX 和 YYYYY 的节点。1 = XXX,2 = YYYYY。

<xsl:for-each select="//table/tr">
   <xsl:if test="count(td) = '1'">
      XXX
   </xsl:if>
   <xsl:for-each select="???">
      all YYYYY up until the next XXX
   </xsl:for-each>
</xsl:for-each>

我有“following-sibling::tr[child::td[following-sibling::td]”,但它与 YYYYY 匹配到最后 - 我如何让它只选择那些直到下一个带有 XXX 的 TR ?

谢谢!

4

2 回答 2

1

一、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:strip-space elements="*"/>

 <xsl:key name="kFollowing" match="tr[td[2]]"
  use="generate-id(preceding-sibling::tr
                                  [not(td[2])]
                                     [1]
                   )"/>

 <xsl:template match="/*">
   <xsl:apply-templates select="tr[not(td[2])]"/>
 </xsl:template>

 <xsl:template match="tr[not(td[2])]">
  <AAA>
    <xsl:value-of select="concat('&#xA;', td/p, '&#xA;')"/>

    <xsl:apply-templates select="key('kFollowing', generate-id())"/>
  </AAA>
 </xsl:template>

 <xsl:template match="p">
  <BBB><xsl:value-of select="."/></BBB>
 </xsl:template>
</xsl:stylesheet>

当此 XSLT 1.0 转换应用于提供的 XML 文档时

<table>
    <tr>
        <td>
            <p>XXX</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>YYYYYY</p>
        </td>
        <td>
            <p>YYYYYY</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>YYYYYY</p>
        </td>
        <td>
            <p>YYYYYY</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>YYYYYY</p>
        </td>
        <td>
            <p>YYYYYY</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>XXX</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>YYYYYY</p>
        </td>
        <td>
            <p>YYYYYY</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>YYYYYY</p>
        </td>
        <td>
            <p>YYYYYY</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>XXX</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>YYYYYY</p>
        </td>
        <td>
            <p>YYYYYY</p>
        </td>
    </tr>
    <tr>
        <td>
            <p>YYYYYY</p>
        </td>
        <td>
            <p>YYYYYY</p>
        </td>
    </tr>
</table>

产生想要的正确结果

<AAA>
XXX
<BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
</AAA>

说明

适当地使用来定义所有tr具有第二个子元素的元素,作为紧接在前的单个子元素td的函数。generate-id()tr


二、XSLT 2.0 解决方案:

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

 <xsl:template match="/*">
  <xsl:for-each-group group-starting-with="tr[not(td[2])]"
       select="tr">
    <xsl:apply-templates select="current-group()[1]"/>
  </xsl:for-each-group>
 </xsl:template>

 <xsl:template match="tr[not(td[2])]">
  <AAA>
    <xsl:value-of select="concat('&#xA;', td/p, '&#xA;')"/>

    <xsl:apply-templates select="current-group()[position() gt 1]"/>
  </AAA>
 </xsl:template>

 <xsl:template match="p">
  <BBB><xsl:value-of select="."/></BBB>
 </xsl:template>
</xsl:stylesheet>

当此转换应用于同一个 XML 文档(如上)时,会产生相同的正确结果

<AAA>
XXX
<BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
</AAA>
<AAA>
XXX
<BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
   <BBB>YYYYYY</BBB>
</AAA>

说明

适当使用xsl:for-each-groupwithgroup-starting-with属性和current-group()函数。

于 2012-04-17T01:50:04.427 回答
0

我一直认为拥有一个完全符合您要求的 XPath 构造会很有用:序列中的所有项目,直到(包括/排除)第一个匹配某个条件的项目。我已经考虑过这样的语法,例如expr until condition. Saxon 和 EXSLT 尝试提供更高阶的扩展函数来满足需求,例如 saxon:leading()。可悲的是,规范中没有任何内容。尽管在 XPath 3.0 中,使用高阶函数可以很容易地自己完成。

您有时可以使用分组 (xsl:for-each-group) 来解决问题,但通常最简单和最优雅的方法是使用递归:即编写一个处理序列中第一项并调用自身的函数如果某些条件为真,则处理下一项。

另一种方法是在序列中搜索与条件匹配的第一个节点,然后使用 subsequence() 选择直到该位置的节点:

<xsl:variable name="positions" select="
  for $i in 1 to count($seq)
  return if (my:condition($seq[$i])) then $i else ()"/>
<xsl:apply-templates select="subsequence($seq, 1, $positions[1])"/>
于 2012-04-17T07:18:11.413 回答