XSLT:如何嵌套平面代码?

时间:2016-11-29 09:17:16

标签: xslt

我尝试嵌套此示例XML文件。我试图使用简单的模板,它会传递当前节点。

<?xml version="1.0" encoding="UTF-8"?>
<chapter>
    <h1>h1</h1>
    <p>text1</p>
    <p>text2</p>
    <p>text3</p>
    <h2>h2</h2>
    <p>text1</p>
    <p>text2</p>
    <p>text3</p>
    <p>text4</p>
    <p>text5</p>
    <p>text6</p>
    <h1>h1</h1>
    <p>text1</p>
    <p>text2</p>
    <p>text3</p>
    <h2>h2</h2>
    <h3>h3</h3>
    <p>text1</p>
    <p>text2</p>
    <p>text3</p>
    <h2>h2</h2>
    <p>text1</p>
    <p>text2</p>
    <p>text3</p>
</chapter>

使用以下样式表:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="2.0">
    <xsl:template match="/">
        <chapter>
        <xsl:apply-templates/>
        </chapter>
    </xsl:template>

    <xsl:template match="h1">
        <level_1>
            <head>
                <xsl:value-of select="."/>
            </head>
            <xsl:apply-templates select="following::*"/>
        </level_1>
    </xsl:template>

    <xsl:template match="h2">
        <head>
            <xsl:value-of select="."/>
        </head>
        <level_2>
            <head>
                <xsl:value-of select="."/>
            </head>
            <xsl:apply-templates select="following::*"/>
        </level_2>
    </xsl:template>

    <xsl:template match="h3">
        <head>
            <xsl:value-of select="."/>
        </head>
        <level_3>
            <head>
                <xsl:value-of select="."/>
            </head>
            <xsl:apply-templates select="following::*"/>
        </level_3>
    </xsl:template>

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

</xsl:stylesheet>

但结果根本不起作用。 following::*不起作用,因为h1也跟随h2,那么模板如何知道,何时关闭?

输出应为:

<chapter>
<level_1>
<head>h1</head>
<text>text1</text>
<text>text2</text>
<text>text3</text>
<level_2>
<head>h2</head>
<text>text1</text>
<text>text2</text>
<text>text3</text>
<text>text4</text>
<text>text5</text>
<text>text6</text>
</level_2>
...
</level_1>
</chapter>

非常感谢!

2 个答案:

答案 0 :(得分:1)

好的,这里有。

定义实用程序函数以测试元素是否为hN:

<xsl:function name="f:isLevel" as="xs:boolean">
  <xsl:param name="h" as="element(*)"/>
  <xsl:param name="level" as="xs:integer"/>
  <xsl:sequence select="name($h) = concat('h', $level)"/>
</xsl:function>

定义一个递归函数,对N级执行分组:

    <xsl:function name="f:group" as="element(*)*">
      <xsl:param name="input" as="element(*)*"/>
      <xsl:param name="level" as="xs:integer"/>
      <xsl:for-each-group 
                    group-starting-with="*[f:isLevel(., $level)]">
         <xsl:choose>
           <xsl:when test="f:isLevel(., $level)">
             <xsl:element name="level_{$level}">
               <head><xsl:copy-of select="."/></head>
               <xsl:sequence select="
                    f:group(remove(current-group(), 1), $level+1)"/>
             </xsl:element>
           </xsl:when>
           <xsl:otherwise>
              <xsl:sequence select="current-group()"/>
           </xsl:otherwise>
         </xsl:choose>
     </xsl:for-each-group>
  </xsl:function>

这是如何工作的?给定类似(p,p,h1,p,p,h1,p)的输入,它创建三组:(p,p,p),(h1,p,p)和(h1,p)。以h1开头的组采用xsl:when分支:它创建一个包含第一个元素(h1)内容的levelN元素,然后递归地将分组应用于h1元素后面的其余组。

初始组(没有前导h1)接受xsl:otherwise分支,只是复制到输出。

当没有名为h {N}的元素时,递归终止:在这种情况下,for-each-group指令只创建一个组,该组在xsl:otherwise分支中处理,不会导致进一步的递归。

要运行此功能,您需要类似

的功能
<xsl:template match="body">
  <xsl:copy-of select="f:group(*, 1)"/>
</xsl:template> 

答案 1 :(得分:1)

这是你可以看到它的一种方式:

XSLT 1.0

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

<xsl:key name="h2" match="h2" use="generate-id(preceding-sibling::h1[1])" />
<xsl:key name="h3" match="h3" use="generate-id(preceding-sibling::h2[1])" />
<xsl:key name="p" match="p" use="generate-id(preceding-sibling::*[starts-with(name(), 'h')][1])" />

<xsl:template match="/chapter">
    <xsl:copy>
        <xsl:apply-templates select="h1"/>
    </xsl:copy>
</xsl:template>     

<xsl:template match="h1">
    <level_1>
        <head>
            <xsl:value-of select="."/>
        </head>
        <xsl:apply-templates select="key('h2', generate-id()) | key('p', generate-id())"/>
    </level_1>
</xsl:template>     

<xsl:template match="h2">
    <level_2>
        <head>
            <xsl:value-of select="."/>
        </head>
        <xsl:apply-templates select="key('h3', generate-id()) | key('p', generate-id())"/>
    </level_2>
</xsl:template> 

<xsl:template match="h3">
    <level_3>
        <head>
            <xsl:value-of select="."/>
        </head>
        <xsl:apply-templates select="key('p', generate-id())"/>
    </level_3>
</xsl:template>     

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

</xsl:stylesheet>