使用XSLT将XML条目转换为指定的次数

时间:2017-11-09 09:58:28

标签: xml xslt

与此类似:Duplicate Element x number of times with XSLT

......但又向前迈进了一步。

我已经有一个xml文档,每个'记录'一次。如果每个记录都有指定的次数需要重复(每个记录不同),那么XSLT是否可以实现?

所以例如:

<?xml version="1.0" standalone="yes"?>
    <Parent>
        <Record name="1">
          <repeat>10</repeat>
            <Child1>Value 1</Child1>
            <Child2>Value 2</Child2>
        </Record>
        <Record name="2">
          <repeat>5</repeat>
            <Child1>Value 1</Child1>
            <Child2>Value 2</Child2>
        </Record>
        <Record name="3">
            <repeat>8</repeat>
            <Child1>Value 1</Child1>
            <Child2>Value 2</Child2>
        </Record>
        <Record name="4">
            <repeat>26</repeat>
            <Child1>Value 1</Child1>
            <Child2>Value 2</Child2>
        </Record>
    </Parent>

XLST可以使用repeat标签复制每条记录,以便记录1插入10次,记录2插入5次,等等......?

我问我可能要为每个学生订购一些自己设计的学校圣诞卡打印版本!

2 个答案:

答案 0 :(得分:1)

使用XSLT 3(由Saxon 9.8所有版本或Altova 2017和2018支持),您可以将其写成紧凑的

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:mode on-no-match="shallow-copy" streamable="yes"/>

    <xsl:output indent="yes"/>

    <xsl:template match="Parent">
        <xsl:copy>
            <xsl:copy-of select="Record!copy-of()!(let $r := . return (1 to repeat)!$r)"/>
        </xsl:copy>
    </xsl:template>

</xsl:stylesheet>

或者用

简单处理Record元素可能更好
<xsl:template match="Record">
    <xsl:copy-of select="copy-of()!(let $r := . return (1 to repeat)!$r)"/>     
</xsl:template>

此外,正如评论中正确指出的那样,正常处理不需要copy-of(),仅用于流式传输,因此如果streamable="yes"上没有xsl:mode,则可以编写模板如

<xsl:template match="Record">
    <xsl:copy-of select="(1 to repeat)!current()"/>     
</xsl:template>

使用XSLT 2可以使用

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:output indent="yes"/>

    <xsl:template match="@* | node()" mode="#all">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()" mode="#current"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Record">
        <xsl:copy-of select="for $n in 1 to repeat return current()"/>     
    </xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

如果使用XSLT 1.0,则升级到XSLT 2.0 ......

但是如果这不可能,那么在XSLT 1.0中你可以使用一个递归的命名模板

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*" />

    <xsl:template match="@*|node()" name="Identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Record">
        <xsl:call-template name="Repeat">
            <xsl:with-param name="repeat" select="repeat" />
        </xsl:call-template>
    </xsl:template>

    <xsl:template match="repeat" />

    <xsl:template name="Repeat">
        <xsl:param name="repeat" />

        <xsl:call-template name="Identity" />
        <xsl:if test="$repeat > 1">
            <xsl:call-template name="Repeat">
                <xsl:with-param name="repeat" select="$repeat - 1" />
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

出于示例目的,我的示例包括从输出中删除repeat

在XSLT 2.0中,你可以这样做..

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
                xmlns:xs="http://www.w3.org/2001/XMLSchema">

    <xsl:output method="xml" indent="yes" />
    <xsl:strip-space elements="*" />

    <xsl:template match="@*|node()" name="Identity">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Record">
        <xsl:variable name="Record" select="." />
        <xsl:for-each select="1 to xs:integer(repeat)">
            <xsl:apply-templates select="$Record" mode="Repeat" />
        </xsl:for-each>
    </xsl:template>

    <xsl:template match="repeat" />

    <xsl:template match="*" mode="Repeat">
        <xsl:call-template name="Identity" />
    </xsl:template>
</xsl:stylesheet>