Previous Page Next Page

Recipe 8.6. Splitting Documents

Problem

You want to partition elements from a single document into subdocuments.

Solution

XSLT 1.0

For XSLT 1.0, you must rely on a widely available but nonstandard extension that allows multiple output documents.[3] The solution determines the level in the document structure to serialize and determines the name of the resulting file. The following stylesheet splits the salesBySalesPerson.xml from Chapter 4 into separate files for each salesperson. The stylesheet works in Saxon. Saxon allows use of the XSLT 1.1 xsl:document element when the stylesheet version is set to 1.1 and some processors support exslt:document from exslt.org.[4]

[3] In XSLT 2.0, this facility is available and uses a new element called xsl:result-document. See Chapter 6 for details.

[4] XSLT 1.1 is no longer an official version. It was abandoned in favor of XSLT 2.0.

If you prefer not to use version 1.1, then you can use the saxon:output extension:

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<xsl:include href="copy.xslt"/>
   
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
   
<xsl:template match="salesperson">
  <xsl:variable name="outFile" 
  select="concat('salesperson.',translate(@name,' ','_'),'.xml')"/>        
  <!-- Non-standard saxon xsl:document! -->
  <xsl:document href="{$outFile}"> 
       <xsl:copy>
              <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
       </xsl:copy>
  </xsl:document>
</xsl:template>
   
<xsl:template match="salesBySalesperson">
  <xsl:apply-templates/>
</xsl:template>
   
</xsl:stylesheet>

Discussion

Although the previous stylesheet is specific to Saxon, the technique works with most XSLT 1.0 processors with only minor changes. Saxon also has the saxon:output extension element (xmlns:saxon = "http://icl.com/saxon"). Xalan uses xalan:redirect (xmlns:xalan = "http://xml.apache.org/xalan").

An interesting variation of splitting also produces an output file that xincludes the generated subfiles:

<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   
<xsl:import href="copy.xslt"/>
   
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
     
<xsl:template match="salesperson">
  <xsl:variable name="outFile" 
      select="concat('salesperson.',translate(@name,' ','_'),'.xml')"/>        
  <xsl:document href="{$outFile}">
       <xsl:copy>
              <xsl:copy-of select="@*"/>
            <xsl:apply-templates/>
       </xsl:copy>
  </xsl:document>
   
  <xi:include href="{$outFile}" 
                        xmlns:xi="http://www.w3.org/2001/XInclude"/>
  
</xsl:template>   
   
</xsl:stylesheet>

If you worry that your XSLT processor might someday recognize XInclude and mistakenly try to include the same file that was just output, you can replace the xi:include literal result element with xsl:element:

  <xsl:element name="xi:include" 
         xmlns:xi="http://www.w3.org/2001/XInclude">
    <xsl:attribute name="href">
      <xsl:value-of select="$outFile"/>
    </xsl:attribute> 
  </xsl:element>

See Also

Recipe 14.1 contains more examples that use multiple output document extensions.


Previous Page Next Page