XSLT将单个xml拆分为多个xmls,保留父元素

时间:2017-03-29 04:22:09

标签: xml xslt split xslt-2.0

我试图根据重复的子元素将一个巨大的xml(大约300MB)拆分成更小的文件。

以下示例说明方案

input.xml

<?xml version="1.0" encoding="UTF-8"?>
<a>
   <b></b>
   <bb></bb>
   <bbb>
        <c>
            <d id="1">
                <x></x>
                <y></y>
            </d>
            <d id="2">
                <x></x>
                <y></y>
            </d>
            <d id="3">
                <x></x>
                <y></y>
            </d>
        </c>
    </bbb>
    <e></e>
    <f></f>
</a>

如上所述,这有一个重复的子元素。基于此元素,通过保持其父元素和属性保持不变,可以获得单独的输出文件。

预期输出 out_1_a.xml

<?xml version="1.0" encoding="UTF-8"?>
<a>
   <b></b>
   <bb></bb>
   <bbb>
        <c>
            <d id="1">
                <x></x>
                <y></y>
            </d>
        </c>
    </bbb>
    <e></e>
    <f></f>
</a>

预期输出 out_2_a.xml

<?xml version="1.0" encoding="UTF-8"?>
<a>
   <b></b>
   <bb></bb>
   <bbb>
        <c>
            <d id="2">
                <x></x>
                <y></y>
            </d>
        </c>
    </bbb>
    <e></e>
    <f></f>
</a>

预期输出 out_3_a.xml

<?xml version="1.0" encoding="UTF-8"?>
<a>
   <b></b>
   <bb></bb>
   <bbb>
        <c>
            <d id="3">
                <x></x>
                <y></y>
            </d>
        </c>
    </bbb>
    <e></e>
    <f></f>
</a>

我的xsl - sample.xsl

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

<xsl:output indent="yes"/>

<xsl:template match="/">
    <xsl:for-each select="a/bbb/c/d">
    <xsl:variable name="i" select="position()" />
    <xsl:result-document method="xml" href="out_{$i}_a.xml">
    <a>
        <b></b>
        <bb></bb>
        <bbb>
            <c>
                <xsl:copy-of select="../@* | ." />
            </c>
        </bbb>
        <e></e>
        <f></f>
    </a>
    </xsl:result-document>
    </xsl:for-each>
</xsl:template> 
</xsl:stylesheet>

这样可以正常工作,我得到了我想要的输出。 但是,我确信有一种更好的方法来实现这一点,而不是硬编码那些父元素,如a,b,bb等。在某些情况下,这些父元素包含属性,它们是动态的。所以硬编码是我想要避免的。有没有更好的方法来解决这个问题?

2 个答案:

答案 0 :(得分:2)

您可以使用:

<xsl:template match="d">
    <xsl:variable name="name" select="generate-id()"/>
    <xsl:variable name="outputposition"><xsl:value-of select="count(preceding::d)+1"></xsl:value-of></xsl:variable>
    <xsl:result-document method="xml" href="out_{$outputposition}_a.xml" indent="yes">
        <xsl:call-template name="spilit">
            <xsl:with-param name="name" select="$name"/>
            <xsl:with-param name="element" select="root()"/>
        </xsl:call-template>
    </xsl:result-document>
</xsl:template>

<xsl:template name="spilit">
    <xsl:param name="name"/>
    <xsl:param name="element"/>
    <xsl:for-each select="$element[descendant-or-self::d[generate-id() eq $name]]">
        <xsl:choose>
            <xsl:when test="self::d[generate-id() = $name]">
                <xsl:copy>
                    <xsl:copy-of select="@*"></xsl:copy-of>
                    <xsl:copy-of select="node()"></xsl:copy-of>
                </xsl:copy>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy-of select="preceding-sibling::*"/>
                <xsl:copy>
                    <xsl:call-template name="spilit">
                        <xsl:with-param name="name" select="$name"/>
                        <xsl:with-param name="element" select="child::*[descendant-or-self::d[generate-id() eq $name]]"/>
                    </xsl:call-template>
                </xsl:copy>
                <xsl:copy-of select="following-sibling::*"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:for-each>
</xsl:template>

答案 1 :(得分:1)

以下 XSLT-2.0 解决方案应该可以轻松完成这项工作:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    version="2.0">
    <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>

    <xsl:template match="@* | node()" mode="createDoc">
        <xsl:param name="id"/>
        <xsl:copy>
            <!-- apply-templates to the attributes and, the desired 'd' child element or all children elements -->
            <xsl:apply-templates select="@*, if(node()[generate-id() = $id]) then node()[generate-id() = $id] else node()" mode="createDoc">
                <xsl:with-param name="id" select="$id"/>
            </xsl:apply-templates>
        </xsl:copy>
    </xsl:template>

    <!-- template to create documents per `d` element -->
    <xsl:template match="/">
        <xsl:for-each select="a/bbb/c/d">
            <xsl:result-document href="out_{@id}_a.xml">
                <xsl:apply-templates select="root(.)" mode="createDoc">
                    <!-- pass the id of the desired element to be copied omitting its siblings-->
                    <xsl:with-param name="id" select="generate-id()"/>
                </xsl:apply-templates>
            </xsl:result-document>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

第二个模板通过将匹配元素的d传递给递归模板(第一个模板),为每个generate-id()元素创建一个文档。

第一个模板,递归复制所有元素。此外,它使用xsl:if仅通过其d复制所需的generate-id()元素,并省略其他兄弟元素。