1

I have an XML doc with containing a catalog of CDs:

<?xml version="1.0"?>
<catalog>
  <cd><title>Greatest Hits 1999</title><artits>Various Artists</artist></cd>
  <cd><title>Greatest Hits 2000</title></cd>
  <cd><title>Best of Christmas</title></cd>
  <cd><title>Foo</title></cd>
  <cd><title>Bar</title></cd>
  <cd><title>Baz</title></cd>
  <!-- hundreds of additional <cd> nodes -->
</catalog>

I want to use XSLT 1.0 to create an excerpt of this XML document, containing only the 1st N <cd> nodes, as well as their parent and children. Let's say N=2; this means I expect the following output:

<?xml version="1.0"?>
<catalog>
  <cd><title>Greatest Hits 1999</title><artits>Various Artists</artist></cd>
  <cd><title>Greatest Hits 2000</title></cd>
</catalog>

I found this answer, from which I adapted the following stylesheet:

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output indent="yes"/>

  <xsl:param name="count" select="2"/>

  <!-- Copy everything, except... -->
  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
  </xsl:template>

  <!-- cd nodes that have a too large index -->
  <xsl:template match="cd[position() &gt;= $count]" />

</xsl:stylesheet>

When I try to apply this stylesheet, I get the following error: Forbidden variable: position() >= $count.
When I replace $count with the literal 2, the output contains the full input document, with hundreds of <cd> nodes.

How can I get an excerpt from my XML document using XSLT that is still valid XML, but simply throws out a bunch of nodes? I'm looking for a generic-ish solution that also works for document structures that are not quite as plain as my example.

derabbink
  • 2,419
  • 1
  • 22
  • 47

1 Answers1

4

Why not simply:

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

<xsl:param name="count" select="2"/>

<xsl:template match="/catalog">
    <xsl:copy>
        <xsl:copy-of select="cd[position() &lt;= $count]"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

If you want to make it generic (which seldom works in practice, because XML documents come in a wide variety of structures), try something like:

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

<xsl:param name="count" select="2"/>

<xsl:template match="/*">
    <xsl:copy>
        <xsl:copy-of select="*[position() &lt;= $count]"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51