1

I am using xslt 2.0, I have following sample xml, I want to get all "header-lightgray" nodes between the two "header-gray" nodes, the problem here is: I don't know how many "header-lightgray" there. so I can not use position() function. and xsl for each don't support break.

<t>
<parent class="header-gray"></parent>
<parent class="header-lightgray"></parent>
<parent class="header-lightgray"></parent>
<parent class="header-lightgray"></parent>
...
<parent class="header-gray"></parent>
<parent class="header-lightgray"></parent>
<parent class="header-lightgray"></parent>
<parent class="header-lightgray"></parent>
<parent class="header-gray"></parent>
</t>

I want go through them and replace all nodes, header-gray with div and header-lightgray with span. the output expected is:

<div class="header-gray">
<span class="header-lightgray"/>
<span class="header-lightgray"/>
<span class="header-lightgray"/>
...
</div>
<div class="header-gray">
<span class="header-lightgray"/>
<span class="header-lightgray"/>
<span class="header-lightgray"/>
</div>

So how to do it?

Any help is appreciated.

Solution I'd like to share according to Jan Vlcinsky's answer:

<xsl:for-each select="/t/parent[@class='header-gray']">
  <xsl:variable name="ns1" select="current()/following-sibling::parent"/>
  <xsl:variable name="ns2" select="current()/following-sibling::parent[@class='header-gray'][1]/preceding-sibling::parent"/>
  <div class="header-gray">
  <xsl:for-each select="$ns1[count(.| $ns2)=count($ns2)]">
     <span class="header-lightgray"/>      
  </xsl:for-each>
  </div>
</xsl:for-each>

I don't verify above code, but it's the same logic in my real code. I didn't use xslt 2.0 way because I found that lxml 2.3 I am using does not support xslt 2.

Monster Z
  • 147
  • 6
  • What do you want to do with these elements once you have them? What version of XSLT are you using? – Borodin May 14 '13 at 13:55
  • I want go through them and replace all nodes, header-gray with div and header-lightgray with span. the span is in the div. – Monster Z May 14 '13 at 14:02

3 Answers3

0

Look to the section "3. Positional grouping problem" at the bottom of page http://gandhimukul.tripod.com/xslt/grouping20.html

Your question seems to be sort of duplicate of: How would you find all nodes between two H3's using XPATH?

Question XPath : select all following siblings until another sibling deals with the same too.

Community
  • 1
  • 1
Jan Vlcinsky
  • 42,725
  • 12
  • 101
  • 98
0

This transformation:

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

 <xsl:template match="/*">
  <t>
   <xsl:for-each-group select="*"
        group-starting-with="parent[@class='header-gray']">
     <div class="header-gray">
       <xsl:apply-templates select="current-group()[position() ne 1]"/>
     </div>
   </xsl:for-each-group>
  </t>
 </xsl:template>

 <xsl:template match="parent">
   <span class="{@class}"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the following XML document (the provided fragment, wrapped into a single top element so that it becomes a well-formed XML document):

<t>
    <parent class="header-gray"></parent>
    <parent class="header-lightgray"></parent>
    <parent class="header-lightgray"></parent>
    <parent class="header-lightgray"></parent>
    <parent class="header-gray"></parent>
    <parent class="header-lightgray"></parent>
    <parent class="header-lightgray"></parent>
    <parent class="header-lightgray"></parent>
    <parent class="header-gray"></parent>
</t>

produces the wanted, correct result:

<t>
   <div class="header-gray">
      <span class="header-lightgray"/>
      <span class="header-lightgray"/>
      <span class="header-lightgray"/>
   </div>
   <div class="header-gray">
      <span class="header-lightgray"/>
      <span class="header-lightgray"/>
      <span class="header-lightgray"/>
   </div>
   <div class="header-gray"/>
</t>
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
-1

Here's one way to do it.

When this XSLT 2.0 solution:

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

   <xsl:template match="/*">
      <t>
         <xsl:apply-templates
           select="parent[@class = 'header-gray'][following-sibling::*]"/>
      </t>
   </xsl:template>

   <xsl:template match="*[@class = 'header-gray']">
      <div class="{@class}">
         <xsl:apply-templates
           select="
             following-sibling::*[@class = 'header-lightgray'] 
               intersect
             following-sibling::*[@class = 'header-gray'][1]/
                 preceding-sibling::*[@class = 'header-lightgray']"/>
      </div>
   </xsl:template>

   <xsl:template match="*[@class = 'header-lightgray']">
      <span class="{@class}"/>
   </xsl:template>

</xsl:stylesheet>

...is applied to the provided XML (wrapped in a top-level element to make the document well-formed):

<t>
  <parent class="header-gray"/>
  <parent class="header-lightgray"/>
  <parent class="header-lightgray"/>
  <parent class="header-lightgray"/>
  <parent class="header-gray"/>
  <parent class="header-lightgray"/>
  <parent class="header-lightgray"/>
  <parent class="header-lightgray"/>
  <parent class="header-gray"/>
</t>

...the desired result is produced:

<t>
  <div class="header-gray">
    <span class="header-lightgray" />
    <span class="header-lightgray" />
    <span class="header-lightgray" />
  </div>
  <div class="header-gray">
    <span class="header-lightgray" />
    <span class="header-lightgray" />
    <span class="header-lightgray" />
  </div>
</t>

This solution makes use of XPath 2.0's intersect operation.

ABach
  • 3,743
  • 5
  • 25
  • 33
  • I'm afraid you have to learn to accept down votes. The vast majority of voters leave no explanation, and I believe it is best that way. My opinion of your answers is that you are too keen just to match the required result, without any thought of the OP's intentions or whether there is a better method. You appear to be using Stack Overflow as a source of exercises. If so, then you should remember that there is no reason to publish your efforts unless you believe they are exemplary. And *please* reconsider your abuse of **bold** in your responses. It is not a style that you should copy. – Borodin May 14 '13 at 19:36