Reputation: 11
We need to make multiple XML documents, each with one "info" element, from a single XML document that has an unknown number of "info" elements. For example, this document:
<alert>
<identifier>2.49.0.1.124.76fea972.2015</identifier>
<info>
<language>en</language>
</info>
<info>
<language>fr</language>
</info>
</alert>
should yield these two documents:
<alert>
<identifier>2.49.0.1.124.76fea972.2015</identifier>
<info>
<language>en</language>
</info>
</alert>
<alert>
<identifier>2.49.0.1.124.76fea972.2015</identifier>
<info>
<language>fr</language>
</info>
</alert>
While pruning siblings of an "info" element, we need to copy all nodes, attributes, namespaces, etc of all ancestors (to the root), as well as all nodes, attributes, namespaces, etc of the particular "info" element.
I am a newbie at XSLT and at a loss for how to do this. Any help would be greatly appreciated!
Upvotes: 0
Views: 61
Reputation: 167716
Using an XSLT 2.0 processor like Saxon 9 or AltovaXML or XmlPrime you can use an approach like this:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="alert/info" mode="split"/>
</xsl:template>
<xsl:template match="info" mode="split">
<xsl:result-document href="language-{language}.xml">
<xsl:apply-templates select="/*">
<xsl:with-param name="info" select="current()" tunnel="yes"/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:template>
<xsl:template match="info">
<xsl:param name="info" tunnel="yes"/>
<xsl:if test=". is $info">
<xsl:next-match/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
If the input is in a certain namespace, then use e.g. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" xpath-default-namespace="urn:oasis:names:tc:emergency:cap:1.2">
.
If you want to run the XSLT independent of the namespace, then use
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* , node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*:alert/*:info" mode="split"/>
</xsl:template>
<xsl:template match="*:info" mode="split">
<xsl:result-document href="language-{language}.xml">
<xsl:apply-templates select="/*">
<xsl:with-param name="info" select="current()" tunnel="yes"/>
</xsl:apply-templates>
</xsl:result-document>
</xsl:template>
<xsl:template match="*:info">
<xsl:param name="info" tunnel="yes"/>
<xsl:if test=". is $info">
<xsl:next-match/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0
Reputation: 163549
Or perhaps this is a bit simpler than Martin's solution:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:template match="/">
<xsl:for-each select="alert/info">
<xsl:result-document href="language-{language}.xml">
<alert>
<xsl:copy-of select="../identifier"/>
<xsl:copy-of select="."/>
</alert>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Upvotes: 0