XSLT:复杂分组的问题

Posted

技术标签:

【中文标题】XSLT:复杂分组的问题【英文标题】:XSLT : Problem with complex grouping 【发布时间】:2011-09-21 20:53:06 【问题描述】:

我正在努力让“for-each-group”工作,我最近切换到 xslt 2,但仍有一些工作要做才能让大家理解它。我正在尝试清除从 Framemaker MIF(平面 xml)收到的一些文件,虽然在大多数情况下数据非常干净,但让我抓狂的是例外情况。我在下面的 xml 中结合了一些典型的例子。我使用的示例与下划线标签有关,原则上文件的构建如下:如果您看到 [Underline/] 标签,则所有以下兄弟姐妹都需要下划线,直到您到达 [EndUnderline/] 标签,所以我的目标是摆脱这两个标签,并将其间的所有兄弟姐妹封装在一个 [u] 标签中。然而问题是,在到达实际的 [EndUnderline/] 标签之前,可能会有后续的 [Underline/] 标签需要被忽略。

让我们试着让上面的内容更明显,这是一个简化的 XML 文件:

<TestFile>
<!-- Para tag containing no underline tags -->
 <Para>
  <Content>[text_not_underlined]</Content>
 </Para>

<!-- correct encapsulation from source -->
<Para>
 <Content>
  <Underline/>[text_to_be_underlined]<EndUnderline/>
  <p>Some test data</p>
 </Content>
</Para>

<!-- extra underline tag that should be ignored -->
<Para>
 <Content>
  <Underline/>[text_to_be_underlined]
  <Underline/>
  <EndUnderline/>
  <p>Some other test data</p>
 </Content>
</Para>

<!-- some extra end underline tags that should be ignored -->
<Para>
 <Content>
  <EndUnderline/>[no_longer_underline]<EndUnderline/>
  <p>: More data</p>
 </Content>
</Para>

</TestFile> 

这是我到现在为止使用 xslt 的地方:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>

<xsl:template match="/">
 <xsl:copy>
  <xsl:apply-templates select="@*|node()"/>
 </xsl:copy>
</xsl:template>

<xsl:template match="@*|node()">
 <xsl:copy>
  <xsl:apply-templates select="@*|node()"/>
 </xsl:copy>
</xsl:template>

<xsl:template match="Content">
 <xsl:copy>
  <xsl:for-each-group select="node()" group-ending-with="EndUnderline">
   <xsl:choose>
    <xsl:when test="current-grouping-key()">
     <xsl:variable name="start" select="current-group()[self::Underline][1]"/>
      <xsl:copy-of select="current-group()[$start >> .]"/>
       <u>
        <xsl:copy-of select="current-group()[. >> $start][not(self::Underline)][not(self::EndUnderline)]"/>
       </u>
      </xsl:when>
     <xsl:otherwise>
    <xsl:copy-of select="current-group()"/>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:for-each-group>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

这就是结果:

<TestFile>

<!-- Para tag containing no underline tags -->
<Para>
 <Content>
  <u/>
 </Content>
</Para>

<!-- correct encapsulation from source -->
<Para>
 <Content>
  <u>[text_to_be_underlined]</u>
  <u/>
 </Content>
</Para>

<!-- extra underline tag that should be ignored -->
<Para>
 <Content>
  <u>[text_to_be_underlined]</u>
  <u/>
 </Content>
</Para>

<!-- some extra end underline tags that should be ignored -->
<Para>
 <Content>
  <u/>
  <u/>
 </Content>
</Para>
</TestFile>

虽然这是我的目标:

<TestFile>
 <!-- Para tag containing no underline tags -->
 <Para>
  <Content>[text_not_underlined]</Content>
 </Para>

<!-- correct encapsulation from source -->
<Para>
 <Content>
  <u>[text_to_be_underlined]</u>
  <p>Some test data</p>
 </Content>
</Para>

<!-- extra underline tag that should be ignored -->
<Para>
 <Content>
  <u>[text_to_be_underlined]</u>
  <p>Some other test data</p>
 </Content>
</Para>
<!-- some extra end underline tags that should be ignored -->
<Para>
 <Content>
  [no_longer_underline]
  <p>: More data</p>
 </Content>
</Para>
</TestFile>

提前感谢任何可以为我指明正确方向的提示!

【问题讨论】:

【参考方案1】:

你说这是一个简化的例子,所以我的解决方案可能不是你想要的。您是否尝试过不使用分组?以下 XSL 似乎给出了正确的结果。

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output indent="yes" />

   <xsl:template match="/ | * | text() | comment() ">
      <xsl:copy>
         <xsl:apply-templates select="* | text() | comment() " />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="p">
      <xsl:copy-of select="." />
   </xsl:template>

   <xsl:template match="Content/text()">
      <xsl:choose>
      <xsl:when test="preceding-sibling::Underline"></xsl:when>
      <xsl:when test="following-sibling::EndUnderline"></xsl:when>
      <xsl:otherwise>
      <xsl:copy-of select="." />
      </xsl:otherwise>
      </xsl:choose>
   </xsl:template>

   <xsl:template match="Content/Underline" />

   <xsl:template match="Content/EndUnderline">
      <xsl:choose>
         <xsl:when test="preceding-sibling::Underline">
            <u><xsl:value-of select="preceding-sibling::text()[1]" /></u>
         </xsl:when>
         <xsl:otherwise>
            <xsl:value-of select="preceding-sibling::text()[1]" />
         </xsl:otherwise>
      </xsl:choose>
   </xsl:template>

</xsl:stylesheet>

【讨论】:

【参考方案2】:

谢谢,但这实际上只有在我假设的开始标签和结束标签之间有一个元素时才有效。

无论如何,在此期间,感谢其他一些有用的互联网人,我找到了答案,所以让我分享一下我们最后的想法:

        <xsl:template match="Content">
    <xsl:copy>
        <xsl:for-each-group select="node()" group-ending-with="EndUnderline">
            <xsl:variable name="start" select="current-group()[self::Underline][1]"/>
            <xsl:choose>
                <xsl:when test="$start">
                    <!-- Content element contains at least one <Underline/> marker element, so we group all between the first <Underline/> tag until the first <EndUnderline/> tag -->
                    <xsl:apply-templates select="current-group()[$start >> .]"/>
                    <!-- Every tag before the first <Underline/> marker gets transformed as standard, all tags between the markers gets encapsulated in a <u> tag -->
                    <u>
                        <xsl:apply-templates select="current-group()[. >> $start][not(self::Underline)][not(self::EndUnderline)]"/>
                    </u>
                </xsl:when>
                <xsl:otherwise>
                    <!-- Apply standard transformation on current group (not containing underline tags...) -->
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>
<!-- Get rif of standalone end tags... -->
<xsl:template match="EndUnderline"/>

【讨论】:

以上是关于XSLT:复杂分组的问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在封闭元素下组织(分组)节点 - XSLT

xslt 的复杂场景

XSLT 转换不适用于复杂的 XML

使用 Python 或 XSLT 将复杂的 XML 转换为 CSV

使用 xslt 将 xml 复杂节点元素拆分为多个节点

如何使用 xslt 获取与复杂条件匹配的所有 xml 项的计数并在终止语句中使用它?