一组连续的兄弟姐妹

时间:2013-01-02 21:15:28

标签: xslt

鉴于兄弟姐妹,其中一些是<row>元素,有些则不是,像这样,

<h />
<row id='v' />
<a />
<b />
<row id='w' />
<d />
<row id='x' />
<row id='y' />
<f />
<r />
<row id='z' />

使用xslt 1.0,我需要按顺序处理它们,但是要像我一样将非行组合在一起,就像这样,

<notRow>
    <h />
</notRow>
<row id='v' />
<notRow>
    <a />
    <b />
</notRow>
<row id='w' />
<notRow>
    <d />
<row id='x' />
<row id='y' />
<notRow>
    <f />
    <r />
</notRow>
<row id='z' />

第一个和最后一个可能是<row>元素,也可能不是。{/ p>

如何?

3 个答案:

答案 0 :(得分:1)

您可以使用键来设置一个技巧,将每个非行元素按前一行(如果有的话)分组,如果没有,则将其父元素分组:

<xsl:key name="elementsFollowingRow"
  match="*[not(self::row)]"
  use="generate-id( (.. | preceding-sibling::row )[last()])" />

并定义一个命名模板,如果当前元素根据键具有任何关联元素,则放入notRow

<xsl:template name="addNotRow">
  <xsl:if test="key('elementsFollowingRow', generate-id())">
    <notRow>
      <xsl:copy-of select="key('elementsFollowingRow', generate-id())" />
    </notRow>
  </xsl:if>
</xsl:template>

然后在您匹配父元素的模板中(包含所有这些row和非row元素的元素可以执行

<xsl:call-template name="addNotRow" />
<xsl:for-each select="row">
  <xsl:copy-of select="." />
  <xsl:call-template name="addNotRow" />
</xsl:for-each>

for-each之外的第一个call-template将处理在第一个notRow之前所需的任何row,并且for-each内的调用将放入任何notRow {{1}}在有问题的行之后需要1}}。

答案 1 :(得分:1)

它可以像这样简短(不需要多次调用模板,xsl:for-eachxsl:if)。这是完整的转型:

<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="*/*[not(self::row)]"
          use="concat(generate-id(..), '+',
                      generate-id(preceding-sibling::row[1])
                     )"/>

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

 <xsl:template priority="2" match=
  "*/*[not(self::row)
   and
     (preceding-sibling::*[1][self::row]
    or not(preceding-sibling::*)
     )]">
  <notRow>
    <xsl:copy-of select=
    "key('kFollowing', concat(generate-id(..), '+',
                               generate-id(preceding-sibling::row[1])
                              ))"/>
  </notRow>
 </xsl:template>

 <xsl:template match="*/*[not(self::row)]"/>
</xsl:stylesheet>

将此转换应用于提供的XML (包装到单个顶部元素中以使其格式正确):

<t>
    <h />
    <row id='v' />
    <a />
    <b />
    <row id='w' />
    <d />
    <row id='x' />
    <row id='y' />
    <f />
    <r />
    <row id='z' />
</t>

产生了想要的正确结果:

<t>
   <notRow>
      <h/>
   </notRow>
   <row id="v"/>
   <notRow>
      <a/>
      <b/>
   </notRow>
   <row id="w"/>
   <notRow>
      <d/>
   </notRow>
   <row id="x"/>
   <row id="y"/>
   <notRow>
      <f/>
      <r/>
   </notRow>
   <row id="z"/>
</t>

<强>更新

OP已经表达了一个额外的要求,即节点需要通过匹配模板进行处理 - 而不仅仅是复制。

这只需要很少的更改

<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="*/*[not(self::row)]"
          use="concat(generate-id(..), '+',
                      generate-id(preceding-sibling::row[1])
                     )"/>

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

 <xsl:template priority="2" match=
  "*/*[not(self::row)
   and
     (preceding-sibling::*[1][self::row]
    or not(preceding-sibling::*)
     )]">
  <notRow>
    <xsl:apply-templates mode="group" select=
    "key('kFollowing', concat(generate-id(..), '+',
                               generate-id(preceding-sibling::row[1])
                              ))"/>
  </notRow>
 </xsl:template>
 <!-- This template can be replaced with whatever processing needed -->
 <xsl:template match="*" mode="group">
  <xsl:copy-of select="."/>
 </xsl:template>
 <xsl:template match="*/*[not(self::row)]"/>
</xsl:stylesheet>

以“group”模式运行的模板应替换为实现精确所需处理的模板。在这种情况下,它会复制匹配的元素 - 但在实际应用程序中,任何想要的处理都会在这里。

答案 2 :(得分:0)

这不太好,但它有效。

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

    <xsl:template match="t">

        <xsl:if test="row[1]/preceding-sibling::*">
            <notRow>
                <xsl:for-each select="row[1]/preceding-sibling::*" >
                    <xsl:copy />
                </xsl:for-each>
            </notRow>
        </xsl:if>

        <xsl:for-each select="row">
            <xsl:copy-of select="."/>
            <xsl:if test="following-sibling::row[1]/preceding-sibling::*[generate-id(preceding-sibling::row[1])=generate-id(current())]">
                <notRow>
                    <xsl:for-each select="following-sibling::row[1]/preceding-sibling::*[generate-id(preceding-sibling::row[1])=generate-id(current())]">
                        <xsl:copy />
                    </xsl:for-each>
                </notRow>
            </xsl:if>
        </xsl:for-each>

        <xsl:if test="row[last()]/following-sibling::*">
            <notRow>
                <xsl:for-each select="row[last()]/following-sibling::*" >
                    <xsl:copy />
                </xsl:for-each>
            </notRow>
        </xsl:if>

    </xsl:template>


</xsl:stylesheet>

在此XML源上

<t>
    <h />
    <i />
    <row id='v' />
    <a />
    <b />
    <row id='w' />
    <d />
    <row id='x' />
    <row id='y' />
    <f />
    <r />
    <row id='z' />
    <i />
</t>

它返回正确的结果:

<notRow>
   <h/>
   <i/>
</notRow>
<row id="v"/>
<notRow>
   <a/>
   <b/>
</notRow>
<row id="w"/>
<notRow>
   <d/>
</notRow>
<row id="x"/>
<row id="y"/>
<notRow>
   <f/>
   <r/>
</notRow>
<row id="z"/>
<notRow>
   <i/>
</notRow>

但似乎应该有更简单的东西。