在group-starting-with中使用变量

时间:2015-01-19 12:08:46

标签: xslt xslt-2.0

我有一个样式表,旨在添加一些目前看起来像这样的结构:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://docbook.org/ns/docbook" xpath-default-namespace="http://docbook.org/ns/docbook">

<xsl:output method="xml" indent="yes"/>

<xsl:template match="book">
    <xsl:copy>
        <xsl:for-each select="@*">
            <xsl:copy/>
        </xsl:for-each>
        <xsl:copy-of select="info"/>
        <xsl:for-each-group select="*" group-starting-with="chapnumber">
            <xsl:choose>
                <xsl:when test="current-group()[1]/name() = 'chapnumber'">
                    <xsl:variable name="chapter_number" select="replace(., '.*([0-9]).*', '$1')"/>
                    <chapter label="{$chapter_number}">
                        <xsl:call-template name="nest_head1">
                            <xsl:with-param name="working_group" select="current-group() except ."/>
                        </xsl:call-template>
                    </chapter>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="nest_head1">
                        <xsl:with-param name="working_group" select="current-group() except ."/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template name="nest_head1">
    <xsl:param name="working_group"/>

    <xsl:for-each-group select="$working_group" group-starting-with="head1">
        <xsl:choose>
            <xsl:when test="current-group()[1]/name() = 'head1'">
                <section>
                    <xsl:call-template name="nest_head2">
                        <xsl:with-param name="working_group" select="current-group()"/>
                    </xsl:call-template>
                </section>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="nest_head2">
                    <xsl:with-param name="working_group" select="current-group()"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each-group>
</xsl:template>

<xsl:template name="nest_head2">
    <xsl:param name="working_group"/>

    <xsl:for-each-group select="$working_group" group-starting-with="head2">
        <xsl:choose>
            <xsl:when test="current-group()[1]/name() = 'head2'">
                <section>
                    <xsl:apply-templates select="current-group()"/>
                </section>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="current-group()"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each-group>
</xsl:template>

<xsl:template match="head1|head2|head3|head4|head5">
    <info>
        <title>
            <xsl:apply-templates/>
        </title>
    </info>
</xsl:template>

<xsl:template match="chaptitle">
    <info>
        <title>
            <xsl:apply-templates/>
        </title>
    </info>
</xsl:template>

<xsl:template match="italic">
    <emphasis role="italic">
        <xsl:apply-templates/>
    </emphasis>
</xsl:template>

<xsl:template match="bold">
    <emphasis role="bold">
        <xsl:apply-templates/>
    </emphasis>
</xsl:template>

<xsl:template match="*">
    <xsl:copy>
        <xsl:for-each select="@*">
            <xsl:copy/>
        </xsl:for-each>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

我不喜欢每个标题级别如何拥有自己的模板(特别是理论上可能有更多级别的标题),并认为它可以浓缩为一个更可用的模板,其中包含标题级别的变量,如此:

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://docbook.org/ns/docbook" xpath-default-namespace="http://docbook.org/ns/docbook">

<xsl:output method="xml" indent="yes"/>

<xsl:template match="book">
    <xsl:copy>
        <xsl:for-each select="@*">
            <xsl:copy/>
        </xsl:for-each>
        <xsl:copy-of select="info"/>
        <xsl:for-each-group select="*" group-starting-with="chapnumber">
            <xsl:choose>
                <xsl:when test="current-group()[1]/name() = 'chapnumber'">
                    <xsl:variable name="chapter_number" select="replace(., '.*([0-9]).*', '$1')"/>
                    <chapter label="{$chapter_number}">
                        <xsl:call-template name="nest_headings">
                            <xsl:with-param name="working_group" select="current-group() except ."/>
                            <xsl:with-param name="heading_level" select="1"/>
                        </xsl:call-template>
                    </chapter>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="nest_headings">
                        <xsl:with-param name="working_group" select="current-group() except ."/>
                        <xsl:with-param name="heading_level" select="1"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:copy>
</xsl:template>

<xsl:template name="nest_headings">
    <xsl:param name="working_group"/>
    <xsl:param name="heading_level"/>

    <xsl:variable name="heading_name">
        <xsl:value-of select="concat('head', string($heading_level))"/>
    </xsl:variable>

    <xsl:if test="$heading_level &lt; 6">
        <xsl:for-each-group select="$working_group" group-starting-with="$heading_name">
            <xsl:choose>
                <xsl:when test="current-group()[1]/name() = $heading_name">
                    <section>
                        <xsl:call-template name="nest_headings">
                            <xsl:with-param name="working_group" select="current-group()"/>
                            <xsl:with-param name="heading_level" select="$heading_level + 1"/>
                        </xsl:call-template>
                    </section>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:call-template name="nest_headings">
                        <xsl:with-param name="working_group" select="current-group()"/>
                        <xsl:with-param name="heading_level" select="$heading_level + 1"/>
                    </xsl:call-template>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:if>
</xsl:template>

<xsl:template match="head1|head2|head3|head4|head5">
    <info>
        <title>
            <xsl:apply-templates/>
        </title>
    </info>
</xsl:template>

<xsl:template match="chaptitle">
    <info>
        <title>
            <xsl:apply-templates/>
        </title>
    </info>
</xsl:template>

<xsl:template match="italic">
    <emphasis role="italic">
        <xsl:apply-templates/>
    </emphasis>
</xsl:template>

<xsl:template match="bold">
    <emphasis role="bold">
        <xsl:apply-templates/>
    </emphasis>
</xsl:template>

<xsl:template match="*">
    <xsl:copy>
        <xsl:for-each select="@*">
            <xsl:copy/>
        </xsl:for-each>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

但是这个样式表无法使用A variable reference is not allowed in an XSLT pattern (except in a predicate)

进行编译

我发现的邮件列表建议group-starting-with="*[name()=$heading_name]。使用它将创建section个节点,但它们将为空。

有没有办法实现我正在寻找的东西?

示例输入:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" role="fullText" xml:lang="en">
    <chapnumber>Chapter 1</chapnumber>
    <chaptitle>Chapter Title</chaptitle>
    <head1>Heading 1</head1>
    <para>1st paragraph</para>
    <head2>Heading 2</head2>
    <para>2nd paragraph</para>
    <head2>Heading 2 2</head2>
    <para>Final paragraph</para>
</book>

预期产出:

<?xml version="1.0" encoding="UTF-8"?>
<book xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink" version="5.0" role="fullText" xml:lang="en">
    <chapter label="1">
        <info>
            <title>Chapter Title</title>
        </info>
        <section>
            <info>
                <title>Heading 1</title>
            </info>
            <para>1st paragraph</para>
            <section>
                <info>
                    <title>Heading 2</title>
                </info>
                <para>2nd paragraph</para>
            </section>
            <section>
                <info>
                    <title>Heading 2 2</title>
                </info>
                <para>Final paragraph</para>
            </section>
        </section>
    </chapter>
</book>

1 个答案:

答案 0 :(得分:3)

您需要确保在分组完成后处理这些项目,因此请使用<xsl:apply-templates select="current-group()"/>中的xsl:otherwise

<xsl:template name="nest_headings">
    <xsl:param name="working_group"/>
    <xsl:param name="heading_level"/>

    <xsl:variable name="heading_name" select="concat('head', $heading_level)"/>

    <xsl:if test="$heading_level &lt; 6">
        <xsl:for-each-group select="$working_group" group-starting-with="*[local-name() eq $heading_name]">
            <xsl:choose>
                <xsl:when test="local-name() eq $heading_name">
                    <section>
                        <xsl:call-template name="nest_headings">
                            <xsl:with-param name="working_group" select="current-group()"/>
                            <xsl:with-param name="heading_level" select="$heading_level + 1"/>
                        </xsl:call-template>
                    </section>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:apply-templates select="current-group()"/>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:for-each-group>
    </xsl:if>
</xsl:template>

test="current-group()[1]/name() = $heading_name"可以缩短为test="local-name() eq $heading_name"。 有关完整示例,请参阅http://xsltransform.net/pPqsHTm/1