2

给定兄弟姐妹,其中一些是<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>元素。

如何?

4

3 回答 3

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>

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

于 2013-01-03T04:24:55.513 回答
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 之外的firstcall-template将处理notRowfirst 之前需要的任何内容row,而 for-each 内部的调用将在相关行之后放入任何notRow需要的内容。

于 2013-01-03T01:05:44.683 回答
0

This isn't pretty, but it works.

<?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>

On this XML source

<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>

it returns the correct result:

<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>

but it does seem there should be something simpler.

于 2013-01-02T23:44:01.577 回答