views:

30

answers:

2

Given the following xml document:

<XML>
<doc1>
</doc1>
<doc2>
</doc2>
<XML>

I was hoping to use an xsl transform to generate 2 XML documents:

<XML>
<doc1>
</doc1>
<XML>

And

<XML>
</doc2>
<doc2>
<XML>

Is this possible?

A: 

We had that problem once and solved it by slightly cheating:

step 1: create one big file containing different xml scripts separated by program instructions or comments. step 2: use a program to cut the file into separate files.

Note that your intermediate result is invalid xml, but the end result is valid.

Example

<doc1>..<doc1>
<!-- SEP -->
<doc2>..<doc2>
<!-- SEP -->
<doc3>..<doc3>
Gamecat
+4  A: 

In XSLT 1.0 it isn't possible to create more than one tree as the output of any transformation, but in XSLT 2.0 this can be done really easy.

In XSLT 1.0 one could use the <exsl:document> extension element of EXSLT.

Or, one can have a transformation, that is provided a global (and externally specified) parameter, containing the element name of the element that must be extracted into a single document:

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

 <xsl:param name="pDocElement" select="'doc1'"/>

 <xsl:template match="/*/*">
  <xsl:if test="name()=$pDocElement">
   <xsl:apply-templates select="." mode="copy"/>
  </xsl:if>
 </xsl:template>

 <xsl:template match="node()" mode="copy">
  <xsl:copy-of select="."/>
 </xsl:template>

 <xsl:template match="text()"/>
</xsl:stylesheet>

when applied on this XML document (based on the provided one):

<t>
    <doc1>
  Doc 1 Content
    </doc1>
    <doc2>
  Doc 2 Content
    </doc2>
    <doc3>
  Doc 3 Content
    </doc3>
</t>

the wanted result is produced:

<doc1>
  Doc 1 Content
</doc1>

And you will run this transformation once for every element, whose sub-tree should be extracted into a separate document.


Here is an XSLT 2.0 solution:

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

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

 <xsl:template match="/*/*[starts-with(name(),'doc')]">
  <xsl:result-document href="{name()}.xml">
   <xsl:apply-templates select="." mode="copy"/>
  </xsl:result-document>
 </xsl:template>

 <xsl:template match="/*">
  <xsl:apply-templates/>
 </xsl:template>
</xsl:stylesheet>

When this transformation is applied on the following XML document (based on the provided one):

<t>
    <doc1>
  Doc 1 Content
    </doc1>
    <doc2>
  Doc 2 Content
    </doc2>
    <doc3>
  Doc 3 Content
    </doc3>
</t>

The result is correctly output to three documents:

Saxon 9.1.0.5J from Saxonica
Java version 1.6.0_21
Stylesheet compilation time: 868 milliseconds
Loading net.sf.saxon.event.MessageEmitter
Writing to file:/C:/Program%20Files/Java/jre6/bin/doc1.xml
Writing to file:/C:/Program%20Files/Java/jre6/bin/doc2.xml
Writing to file:/C:/Program%20Files/Java/jre6/bin/doc3.xml
Execution time: 151 milliseconds
Memory used: 11467936
NamePool contents: 18 entries in 18 chains. 6 prefixes, 6 URIs
Dimitre Novatchev
@Dimitre: +1 Excellent answer including XSLT 1.0 without extensions solution!
Alejandro