我有一个如下的xml文件,
<doc>
<para>111111111111</para>
<!--start div1-->
<para>2222222</para>
<para>3333333</para>
<!--start div2-->
<para>4444444</para>
<para>5555555</para>
<para>6666666</para>
<!--start div3-->
<para>7777777</para>
<para>8888888</para>
<!--end div3-->
<para>9999999</para>
<para>1010110</para>
<!--end div2-->
<para>11,11,11</para>
<para>12,12,12,</para>
<!--end div1-->
<para>13,13,13</para>
</doc>
我正在从此输入进行XSLT转换,我需要将这些元素放在嵌套的<div>
元素上。在上一阶段添加了注释,以标识<div>
元素应该去的位置。在给定的示例中,存在三个嵌套的div
元素,但是可以更改。我的预期输出是
<doc>
<para>111111111111</para>
<div>
<!--start div1-->
<para>2222222</para>
<para>3333333</para>
<div>
<!--start div2-->
<para>4444444</para>
<para>5555555</para>
<para>6666666</para>
<div>
<!--start div3-->
<para>7777777</para>
<para>8888888</para>
<!--end div3-->
</div>
<para>9999999</para>
<para>1010110</para>
<!--end div2-->
</div>
<para>11,11,11</para>
<para>12,12,12,</para>
<!--end div1-->
</div>
<para>13,13,13</para>
</doc>
我无法编写三个嵌套的<for-each>
组,因为这些嵌套的<div>
元素可以在其他输入中进行更改。 (可以转到嵌套的第四或第五级,等等。)
我尝试过以下方法,但是仅添加了第一个<div>
元素,
<xsl:template match="doc">
<doc>
<xsl:for-each-group select="*|comment()" group-adjacent="
if (self::comment())
then
substring-after(self::comment(),' ')
else
local-name()">
<xsl:for-each-group select="current-group()" group-starting-with="*">
<div id="{local-name()}-{position()}">
<xsl:copy-of select="current-group()" />
</div>
</xsl:for-each-group>
</xsl:for-each-group>
</doc>
</xsl:template>
有人可以建议我实现这些嵌套的<div>
元素的方法吗?或如何在XSLT中递归调用<for-each>
?
答案 0 :(得分:2)
您可以在模板或函数中实现递归,以下是一个函数,因为它允许更紧凑的语法:
<xsl:function name="mf:group" as="node()*">
<xsl:param name="input" as="node()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$input" group-starting-with="comment()[. = 'start div' || $level]">
<xsl:choose>
<xsl:when test="self::comment()[. = 'start div' || $level]">
<xsl:for-each-group select="current-group()" group-ending-with="comment()[. = 'end div' || $level]">
<xsl:choose>
<xsl:when test="current-group()[last()][self::comment()[. = 'end div' || $level]]">
<div>
<xsl:apply-templates select="."/>
<xsl:sequence select="mf:group(current-group()[not(position() = (1, last()))], $level + 1)"/>
<xsl:apply-templates select="current-group()[last()]"/>
</div>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
完整的示例是(XSLT 3,但是对于XSLT 2,您只需要使用||
调用来代替对concat
运算符的使用):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="3.0">
<xsl:function name="mf:group" as="node()*">
<xsl:param name="input" as="node()*"/>
<xsl:param name="level" as="xs:integer"/>
<xsl:for-each-group select="$input" group-starting-with="comment()[. = 'start div' || $level]">
<xsl:choose>
<xsl:when test="self::comment()[. = 'start div' || $level]">
<xsl:for-each-group select="current-group()" group-ending-with="comment()[. = 'end div' || $level]">
<xsl:choose>
<xsl:when test="current-group()[last()][self::comment()[. = 'end div' || $level]]">
<div>
<xsl:apply-templates select="."/>
<xsl:sequence select="mf:group(current-group()[not(position() = (1, last()))], $level + 1)"/>
<xsl:apply-templates select="current-group()[last()]"/>
</div>
</xsl:when>
<xsl:otherwise>
<xsl:sequence select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:function>
<xsl:output indent="yes"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="doc">
<xsl:copy>
<xsl:sequence select="mf:group(node(), 1)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
答案 1 :(得分:0)
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="comment()[.='start div1' or .='start div2' or .='start div3' or .='end div3' or .='end div2' or .='end div1']">
<xsl:choose>
<xsl:when test=". ='start div1'">
<xsl:text disable-output-escaping="yes"><div></xsl:text>
<xsl:text disable-output-escaping="yes"><!--start div1--></xsl:text>
</xsl:when>
<xsl:when test=". ='start div2'">
<xsl:text disable-output-escaping="yes"><div></xsl:text>
<xsl:text disable-output-escaping="yes"><!--start div2--></xsl:text>
</xsl:when>
<xsl:when test=". ='start div3'">
<xsl:text disable-output-escaping="yes"><div></xsl:text>
<xsl:text disable-output-escaping="yes"><!--start div3--></xsl:text>
</xsl:when>
<xsl:when test=". ='end div3'">
<xsl:text disable-output-escaping="yes"><!--end div3--></xsl:text>
<xsl:text disable-output-escaping="yes"></div></xsl:text>
</xsl:when>
<xsl:when test=". ='end div2'">
<xsl:text disable-output-escaping="yes"><!--end div2--></xsl:text>
<xsl:text disable-output-escaping="yes"></div></xsl:text>
</xsl:when>
<xsl:when test=". ='end div1'">
<xsl:text disable-output-escaping="yes"><!--end div1--></xsl:text>
<xsl:text disable-output-escaping="yes"></div></xsl:text>
</xsl:when>
</xsl:choose>
</xsl:template>
You may try like this