[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

Re: [xsl] Fetch a sequence of nodes from input file


Subject: Re: [xsl] Fetch a sequence of nodes from input file
From: Martin Honnen <Martin.Honnen@xxxxxx>
Date: Wed, 23 Sep 2009 19:25:21 +0200

rowan@xxxxxxxxxxxxxxxxxxxxx wrote:

<?xml version="1.0" encoding="UTF-8"?>
<items>
   <item>
      <name>First</name>
      <level>0</level>
      <ref/>
      <contents>First contents.</contents>
   </item>
   <item>
      <name>Second</name>
      <level>1</level>
      <ref/>
      <contents>Second contents.</contents>
   </item>
   <item>
      <name>Third</name>
      <level>2</level>
      <ref/>
      <contents>Third contents.</contents>
   </item>
   <item>
      <name>Fourth</name>
      <level>3</level>
      <ref/>
      <contents>Fourth contents.</contents>
   </item>
   <item>
      <name>Fifth</name>
      <level>1</level>
      <ref/>
      <contents>Fifth contents.</contents>
   </item>
   <item>
      <name>Sixth</name>
      <level>2</level>
      <ref>Third</ref>
      <contents>Sixth contents.</contents>
   </item>
   <item>
      <name>Seventh</name>
      <level>1</level>
      <ref/>
      <contents>Seventh contents.</contents>
   </item>
</items>

When I find an item with an empty <ref> element, I want to just copy it to
the putput. But when I find one with a non-empty <ref> element, I want to
copy <item>s in sequence, starting with the one whose <name> matches the
<ref> of the source <item> (I'll call this the start item), and finishing
with the last one whose <level> value is greater than the <level> of the
start item. So I want to stop just before the <item> whose <level> is <=
the <level> of the start item.

What I want to produce from the above file is:

<?xml version="1.0" encoding="UTF-8"?>
<items>
   <item>
      <name>First</name>
      <level>0</level>
      <ref/>
      <contents>First contents.</contents>
   </item>
   <item>
      <name>Second</name>
      <level>1</level>
      <ref/>
      <contents>Second contents.</contents>
   </item>
   <item>
      <name>Third</name>
      <level>2</level>
      <ref/>
      <contents>Third contents.</contents>
   </item>
   <item>
      <name>Fourth</name>
      <level>3</level>
      <ref/>
      <contents>Fourth contents.</contents>
   </item>
   <item>
      <name>Fifth</name>
      <level>1</level>
      <ref/>
      <contents>Fifth contents.</contents>
   </item>
   <item>
      <name>Third</name>
      <level>2</level>
      <ref/>
      <contents>Third contents.</contents>
   </item>
   <item>
      <name>Fourth</name>
      <level>3</level>
      <ref/>
      <contents>Fourth contents.</contents>
   </item>
   <item>
      <name>Seventh</name>
      <level>1</level>
      <ref/>
      <contents>Seventh contents.</contents>
   </item>
</items>

Assuming XSLT 2.0 I tried to implement your description as follows:


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

<xsl:key name="k1" match="item" use="name"/>

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

<xsl:template match="item[normalize-space(ref)]">
<xsl:variable name="si" select="key('k1', ref)[1]"/>
<xsl:copy-of select="$si | $si/following-sibling::item[. &lt;&lt; $si/following-sibling::item[level &lt;= $si/level][1]]"/>
</xsl:template>


</xsl:stylesheet>

For the input sample you provided that produces the result you describe.

--

	Martin Honnen
	http://msmvps.com/blogs/martin_honnen/


Current Thread
Keywords