我试图根据重复的子元素将一个巨大的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等。在某些情况下,这些父元素包含属性,它们是动态的。所以硬编码是我想要避免的。有没有更好的方法来解决这个问题?
答案 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()
元素,并省略其他兄弟元素。