XSLT 2.0 - 在非嵌套输入的输出中嵌套元素

时间:2011-02-15 22:42:31

标签: xslt position elements xslt-2.0 counting

我正在处理一个样式表,它从输入文件中以无层次结构输出。输入文件的每个元素都是彼此的兄弟,每个“标题”元素表示不同的部分。

示例输入文件(每个标题下的实际部分大约是此时间的10倍):

<Header>  
     Header1
</Header>
<Sub1>
     Sub1 first
</Sub1>
<Sub1>
     Sub1 second
</Sub1>
<Sub2>
     Sub2 first, Sub1 second
</Sub2>
<Sub1>
     Sub1 third
</Sub1>
<Sub2>
     Sub2 first, Sub1 third
</Sub2>

<Header>
     Header2
</Header>
Etc...

以上输入的输出应如下所示:

<Header>
     Header1
     <Step>
         Sub1 first
     </Step>
     <Step>
         Sub1 second
         <Step>
             Sub2 first, Sub1 second
         </Step>
     </Step>
     <Step>
         Sub1 third
         <Step>
             Sub2 first, Sub1 third
         </Step>
     </Step>
</Header>

<Header>
    Header2
    Etc.....
</Header>

现在,我能够将输出提升到“Sub2 first,sub1 second”。当我知道下一个元素不是另一个Sub2或更深的子元素(Sub3)时,我从我的Sub2模板中断,返回到Sub1模板。从这里开始,我的位置变量仍然具有“Sub1 second”的位置。我每次调用模板时都会处理定位信息 - 所以我按文档顺序排列“Sub2 first,sub1 second”的所有当前位置,但是一旦我从模板中断回Sub1的模板,我就丢失了这些信息。我似乎无法按文档顺序获取当前位置以确定真正的下一个元素是什么。可修改的全局参数或变量将是理想的,但我知道在XSLT中这是不可能的。

我不确定如何实现这一目标。任何建议都会很棒!

3 个答案:

答案 0 :(得分:1)

此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:template match="/*">
  <t>
   <xsl:apply-templates select="*[1]">
    <xsl:with-param name="pScope" select="*"/>
    <xsl:with-param name="pElemName" select="name(*[1])"/>
   </xsl:apply-templates>
  </t>
 </xsl:template>

 <xsl:template match="*">
  <xsl:param name="pScope"/>
  <xsl:param name="pElemName" select="'Step'"/>

    <xsl:for-each-group select="$pScope"
        group-starting-with="*[name()= name($pScope[1])]">
       <xsl:element name="{$pElemName}">
         <xsl:value-of select="."/>
         <xsl:apply-templates select="current-group()[2]">
          <xsl:with-param name="pScope" select=
           "current-group()[position() > 1]"/>
         </xsl:apply-templates>
       </xsl:element>
    </xsl:for-each-group>
 </xsl:template>
</xsl:stylesheet>

应用于此XML文档(基于提供的,但包含在顶部元素中并添加了一个具有不同名称的标头):

<t>
    <Header>Header1</Header>
    <Sub1>Sub1 first</Sub1>
    <Sub1>Sub1 second</Sub1>
    <Sub2>Sub2 first, Sub1 second</Sub2>
    <Sub1>Sub1 third</Sub1>
    <Sub2>Sub2 first, Sub1 third</Sub2>
    <Header>Header2</Header>
    <x>Sub1 first</x>
    <x>Sub1 second</x>
    <y>Sub2 first, Sub1 second</y>
    <x>Sub1 third</x>
    <z>Sub2 first, Sub1 third</z>
</t>

生成所需的正确输出

<t>
   <Header>Header1<Step>Sub1 first</Step>
      <Step>Sub1 second<Step>Sub2 first, Sub1 second</Step>
      </Step>
      <Step>Sub1 third<Step>Sub2 first, Sub1 third</Step>
      </Step>
   </Header>
   <Header>Header2<Step>Sub1 first</Step>
      <Step>Sub1 second<Step>Sub2 first, Sub1 second</Step>
      </Step>
      <Step>Sub1 third<Step>Sub2 first, Sub1 third</Step>
      </Step>
   </Header>
</t>

解释

  1. <xsl:for-each-group>group-starting-with属性一起使用。

  2. 细粒度处理 - 模板应用于current-group()的第二个元素,下一个分组的范围作为参数传递。

答案 1 :(得分:0)

只是为了好玩一个XSLT 1.0样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*">
        <xsl:param name="pNames" select="'|'"/>
        <xsl:if test="not(contains($pNames,concat('|',name(),'|')))">
            <xsl:variable name="vNext" select="following-sibling::*[1]"/>
            <xsl:copy>
                <xsl:apply-templates select="node()[1]"/>
                <xsl:apply-templates select="$vNext">
                    <xsl:with-param name="pNames"
                                    select="concat($pNames,name(),'|')"/>
                </xsl:apply-templates>
            </xsl:copy>
            <xsl:apply-templates select="$vNext" mode="search">
                <xsl:with-param name="pNames" select="$pNames"/>
                <xsl:with-param name="pSearch" select="name()"/>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>
    <xsl:template match="*" mode="search">
        <xsl:param name="pNames"/>
        <xsl:param name="pSearch"/>
        <xsl:if test="not(contains($pNames,concat('|',name(),'|')))">
            <xsl:choose>
                <xsl:when test="name()=$pSearch">
                    <xsl:apply-templates select=".">
                        <xsl:with-param name="pNames" select="$pNames"/>
                    </xsl:apply-templates>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="following-sibling::*[1]"
                                         mode="search">
                        <xsl:with-param name="pNames" select="$pNames"/>
                        <xsl:with-param name="pSearch" select="$pSearch"/>
                    </xsl:apply-templates>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

通过良好的输入:

<root>
    <Header>Header1</Header>
    <Sub1>Sub1 first</Sub1>
    <Sub1>Sub1 second</Sub1>
    <Sub2>Sub2 first, Sub1 second</Sub2>
    <Sub1>Sub1 third</Sub1>
    <Sub2>Sub2 first, Sub1 third</Sub2>
    <Header>Header2</Header>
</root>

输出:

<root>
    <Header>Header1
        <Sub1>Sub1 first</Sub1>
        <Sub1>Sub1 second
            <Sub2>Sub2 first, Sub1 second</Sub2>
        </Sub1>
        <Sub1>Sub1 third
            <Sub2>Sub2 first, Sub1 third</Sub2>
        </Sub1>
    </Header>
    <Header>Header2</Header>
</root>

使用名称替换和具有多个名称的一个级别的新requeriment,此样式表:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:r="rules"
 exclude-result-prefixes="r">
    <r:r m="|Header|">Header</r:r>
    <r:r m="|Sub1|">Step</r:r>
    <r:r m="|Sub2|">Step</r:r>
    <xsl:variable name="vRules" select="document('')/*/r:r"/>
    <xsl:template match="*">
        <xsl:param name="pNames" select="'|'"/>
        <xsl:variable name="vName" select="concat('|',name(),'|')"/>
        <xsl:if test="not(contains($pNames,$vName))">
            <xsl:variable name="vNext" select="following-sibling::*[1]"/>
            <xsl:variable name="vMatch"
                          select="$vRules[contains(@m,$vName)]"/>
            <xsl:variable name="vReplace"
                 select="concat($vMatch,name((.)[not($vMatch)]))"/>
            <xsl:variable name="vSearch"
                          select="concat($vMatch/@m,$vName)"/>
            <xsl:element name="{$vReplace}">
                <xsl:apply-templates select="node()[1]"/>
                <xsl:apply-templates select="$vNext">
                    <xsl:with-param name="pNames"
                                    select="concat($pNames,$vSearch)"/>
                </xsl:apply-templates>
            </xsl:element>
            <xsl:apply-templates select="$vNext" mode="search">
                <xsl:with-param name="pNames" select="$pNames"/>
                <xsl:with-param name="pSearch" select="$vSearch"/>
            </xsl:apply-templates>
        </xsl:if>
    </xsl:template>
    <xsl:template match="*" mode="search">
        <xsl:param name="pNames"/>
        <xsl:param name="pSearch"/>
        <xsl:variable name="vName" select="concat('|',name(),'|')"/>
        <xsl:choose>
            <xsl:when test="contains($pNames,$vName)"/>
            <xsl:when test="contains($pSearch,$vName)">
                <xsl:apply-templates select=".">
                    <xsl:with-param name="pNames" select="$pNames"/>
                </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="following-sibling::*[1]"
                                         mode="search">
                    <xsl:with-param name="pNames" select="$pNames"/>
                    <xsl:with-param name="pSearch" select="$pSearch"/>
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

输出:

<root>
    <Header>Header1
        <Step>Sub1 first</Step>
        <Step>Sub1 second
            <Step>Sub2 first, Sub1 second</Step>
        </Step>
        <Step>Sub1 third
            <Step>Sub2 first, Sub1 third</Step>
        </Step>
    </Header>
    <Header>Header2</Header>
</root>

注意:同一级别的同义词应位于r:r/@m

答案 2 :(得分:-1)

你的输入xml实际上不是一个xml,所以我添加了一个根节点。我建议不要使用xslt来进行这种处理。但如果你必须 - 这是让你开始的东西。

顺便说一下。您的小节是否有任何类型的ID?

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
                xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/Root">
    <Root>
      <xsl:apply-templates select="Header"/>
    </Root>
  </xsl:template>

  <xsl:template match="Header">
    <Header>
      <xsl:copy-of select="text()"/>
      <xsl:variable name="current" select="."/>
      <xsl:apply-templates select="/Root/Sub1[preceding-sibling::Header[1]/. = $current]"/>
    </Header>
  </xsl:template>

  <xsl:template match="Sub1">
    <Step>
      <xsl:copy-of select="text()"/>
      <xsl:variable name="current" select="."/>
      <xsl:apply-templates select="/Root/Sub2[preceding-sibling::Sub1[1]/. = $current]"/>
    </Step>
  </xsl:template>

  <xsl:template match="Sub2">
    <Step>
      <xsl:copy-of select="text()"/>
      <!-- todo -->
    </Step>
  </xsl:template>

</xsl:stylesheet>