Wrox Programmer Forums

Need to download code?

View our list of code downloads.

Go Back   Wrox Programmer Forums > XML > XSLT
Password Reminder
Register
| FAQ | Members List | Calendar | Search | Today's Posts | Mark Forums Read
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of tens of thousands of software programmers and website developers including Wrox book authors and readers. As a guest, you can read any forum posting. By joining today you can post your own programming questions, respond to other developers’ questions, and eliminate the ads that are displayed to guests. Registration is fast, simple and absolutely free .
DRM-free e-books 300x50
Reply
 
Thread Tools Search this Thread Display Modes
  #1 (permalink)  
Old March 2nd, 2010, 07:34 AM
Authorized User
 
Join Date: Feb 2009
Location: Gurgaon
Posts: 31
Thanks: 6
Thanked 1 Time in 1 Post
Send a message via MSN to anil_yadav26@hotmail.com
Default [XSLT] Index Page Range

Hi,

I have an xml file in which pages are appearing under the <link> tag. I have to make the page range for continuous appearing pages e.g. in ITEM 3 pages are appearing like this:

<link>410</link><link>411</link><link>412</link><link>413</link><link>420</link>

and need to transform like this:

<link>410--413</link><link></link><link></link><link></link><link>420</link>

Can anybody suggest me to write the XSLT, XQuery for the same.

NOTE: Please note that all the <link> tags are required because they contains some attribute IDs which can be use in future for updates.

Source XML File

Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book>
<endmatter>
<index>
<title><emphasis type="bold">European Court of Justice: Cases in case number order</emphasis></title>
<index-list>
<index-item><index-entry>1/58 Stork v High ECR 17</index-entry>&#x2002;<link>232--233</link></index-item>
<index-item><index-entry>42 SNUPAT v High Authority</index-entry>&#x2002;<link>410</link><link>411</link><link>412</link><link>413</link><link>420</link><link>448</link><link>449</link></index-item>
<index-item><index-entry>16/62 Confration Nationale </index-entry>&#x2002;<link>520</link><link>521</link><link>524</link><link>525</link><link>530</link><link>595</link></index-item>
</index-list>
</index>
</endmatter>
</book>
Output required

Code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<book>
<endmatter>
<index>
<title><emphasis type="bold">European Court of Justice: Cases in case number order</emphasis></title>
<index-list>
<index-item><index-entry>1/58 Stork v High ECR 17</index-entry>&#x2002;<link>232--233</link></index-item>
<index-item><index-entry>42 SNUPAT v High Authority</index-entry>&#x2002;<link>410--413</link><link></link><link></link><link></link><link>420</link><link>448-449</link><link></link></index-item>
<index-item><index-entry>16/62 Confration Nationale </index-entry>&#x2002;<link>520--521</link><link></link><link>524--525</link><link></link><link>530</link><link>595</link></index-item>
</index-list>
</index>
</endmatter>
</book>
I'll really appreciate any help on this.

Regards,
Anil Yadav

Last edited by anil_yadav26@hotmail.com; March 2nd, 2010 at 08:00 AM..
Reply With Quote
  #2 (permalink)  
Old March 2nd, 2010, 08:12 AM
mhkay's Avatar
Wrox Author
Points: 18,481, Level: 59
Points: 18,481, Level: 59 Points: 18,481, Level: 59 Points: 18,481, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,960
Thanks: 0
Thanked 292 Times in 287 Posts
Default

Assuming you're using XSLT 2.0, there's a neat trick for this discovered by David Carlisle (if I remember right).

Code:
<xsl:for-each-group select="link" group-adjacent="number(.) - position()">
   <xsl:for-each select="current-group()">
      <xsl:choose>
       <xsl:when test="position() = 1 and count(current-group()) = 1">
         <xsl:copy-of select="."/>
       </xsl:when>
       <xsl:when test="position() = 1">
          <link><xsl:value-of select="concat(., '--', current-group()[last()]"/></link>
       </xsl:when>
       <xsl:otherwise>
            <link/>
       </
      </
   </
</
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
Reply With Quote
  #3 (permalink)  
Old March 2nd, 2010, 08:46 AM
Authorized User
 
Join Date: Feb 2009
Location: Gurgaon
Posts: 31
Thanks: 6
Thanked 1 Time in 1 Post
Send a message via MSN to anil_yadav26@hotmail.com
Default

Hi Michael,

Thanks for your response!

I run the suggested code but its creating the page range for first and last node of group whereas I need range only for continuous page numbers. In the below example (red) you can see input xml file coding and blue contains output xml coding. The blue part contains two information first is continuous page range and independent page number.

<link>410</link><link>411</link><link>412</link><link>413</link><link>420</link>

and need to transform like this:

<link>410--413</link><link></link><link></link><link></link><link>420</link>


XSLT
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs" 
    version="2.0">
 <xsl:template match="index-item">
  <xsl:for-each-group select="link" group-adjacent="number(link) - position()">
  <xsl:for-each select="current-group()">
   <xsl:choose>
    <xsl:when test="position() = 1 and count(current-group()) = 1">
     <xsl:copy-of select="."/>
    </xsl:when>
    <xsl:when test="position() = 1">
     <link><xsl:value-of select="concat(., '--', current-group()[last()])"/></link>
    </xsl:when>
    <xsl:otherwise>
     <link/>
    </xsl:otherwise>
   </xsl:choose>
  </xsl:for-each>
 </xsl:for-each-group>
  </xsl:template>
</xsl:stylesheet>
Could you please look into this and do the needful.

Thanks,
Anil Yadav
Reply With Quote
  #4 (permalink)  
Old March 2nd, 2010, 08:54 AM
Friend of Wrox
Points: 6,676, Level: 34
Points: 6,676, Level: 34 Points: 6,676, Level: 34 Points: 6,676, Level: 34
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Nov 2007
Location: Germany
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

What Michael posted outlines the grouping for those 'link' elements, as you have other child elements and also some link elements which already have a "range" I think you need an additional grouping construct as follows:
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="index-item">
    <xsl:copy>
      <xsl:for-each-group select="node()" group-adjacent="boolean(self::link[matches(., '^[0-9]+$')])">
        <xsl:choose>
          <xsl:when test="current-grouping-key()">
            <xsl:for-each-group select="current-group()" group-adjacent="number(.) - position()">
              <xsl:for-each select="current-group()">
                <xsl:choose>
                  <xsl:when test="position() eq 1 and count(current-group()) eq 1">
                    <xsl:copy-of select="current-group()"/>
                  </xsl:when>
                  <xsl:when test="position() eq 1">
                    <link><xsl:value-of select="concat(current-group()[1], '--', current-group()[last()])"/></link>
                  </xsl:when>
                  <xsl:otherwise>
                    <link/>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:for-each>
            </xsl:for-each-group>
          </xsl:when>
          <xsl:otherwise>
            <xsl:copy-of select="current-group()"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:for-each-group>
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
Reply With Quote
The Following User Says Thank You to Martin Honnen For This Useful Post:
anil_yadav26@hotmail.com (March 2nd, 2010)
  #5 (permalink)  
Old March 2nd, 2010, 09:50 AM
Authorized User
 
Join Date: Feb 2009
Location: Gurgaon
Posts: 31
Thanks: 6
Thanked 1 Time in 1 Post
Send a message via MSN to anil_yadav26@hotmail.com
Thumbs up

Hi Martin,

Thanks for your reply. Now, its working perfectly. Thanks a lot!!!

I have one more query regarding the same input XML. Actually I have to filter the XML with following steps:

1. Sorting the <link> nodes
2. Removing the content of duplicate nodes
3. Making range

Now I have three different XSLT for all the TASK. Is it possible to merge all these XSLT into one?

Thanks in advance.


Thanks,
Anil Yadav



1. Sorting the <link> nodes
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs" 
    version="2.0">
 
 <xsl:template match="@*|node()">
   <xsl:copy>
    <xsl:apply-templates select="@*|node()"/>
   </xsl:copy>
 </xsl:template>
 
<xsl:template match="index-item">
 <index-item>
  <xsl:copy-of select="@*"/>
  <xsl:if test="index-entry">
    <xsl:apply-templates select="index-entry"/>
   </xsl:if>
  <xsl:for-each select="link">
   <xsl:sort select="."/>
   <link>
    <xsl:copy-of select="@*"/>
    <xsl:value-of select="."/>
   </link>
  </xsl:for-each>
 </index-item>
 </xsl:template>
</xsl:stylesheet>

2. Removing the content of duplicate nodes

Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    exclude-result-prefixes="xs" 
    version="2.0">
 <xsl:template match="index-item">
  <index-item>
   <xsl:copy-of select="@*"/>
   <xsl:if test="index-entry">
    <xsl:apply-templates select="index-entry"/>
   </xsl:if>
   <xsl:for-each select="link">
    <xsl:sort select="."/>
    <link>
     <xsl:copy-of select="@*"/>
     <xsl:if test="not(node()) or not(preceding-sibling::node()[.=string(current())])">
      <xsl:if test="position()!=last()">
       <xsl:value-of select="."/><xsl:text>, </xsl:text>
     </xsl:if>
      <xsl:if test="position()=last()">
       <xsl:value-of select="."/>
      </xsl:if>
     </xsl:if>
    </link>
   </xsl:for-each>
  </index-item>
 </xsl:template>
</xsl:stylesheet>

3. Making range

Code:
<xsl:template match="@* | node()">
  <xsl:copy>
   <xsl:apply-templates select="@* | node()"/>
  </xsl:copy>
 </xsl:template>
 
 <xsl:template match="index-item">
  <xsl:copy>
   <xsl:for-each-group select="node()" group-adjacent="boolean(self::link[matches(., '^[0-9]+$')])">
    <xsl:choose>
     <xsl:when test="current-grouping-key()">
      <xsl:for-each-group select="current-group()" group-adjacent="number(.) - position()">
       <xsl:for-each select="current-group()">
        <xsl:choose>
         <xsl:when test="position() eq 1 and count(current-group()) eq 1">
          <xsl:copy-of select="current-group()"/>
         </xsl:when>
         <xsl:when test="position() eq 1">
          <link>
           <xsl:copy-of select="@*"/>
           <xsl:value-of select="concat(current-group()[1], '--', current-group()[last()])"/>
          </link>
         </xsl:when>
         <xsl:otherwise>
          <link>
           <xsl:copy-of select="@*"/>
          </link>
         </xsl:otherwise>
        </xsl:choose>
       </xsl:for-each>
      </xsl:for-each-group>
     </xsl:when>
     <xsl:otherwise>
      <xsl:copy-of select="current-group()"/>
     </xsl:otherwise>
    </xsl:choose>
   </xsl:for-each-group>
  </xsl:copy>
 </xsl:template>
Reply With Quote
  #6 (permalink)  
Old March 2nd, 2010, 10:01 AM
Friend of Wrox
Points: 6,676, Level: 34
Points: 6,676, Level: 34 Points: 6,676, Level: 34 Points: 6,676, Level: 34
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Nov 2007
Location: Germany
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

With XSLT 2.0 it is easily possible to combine several transformation steps into one stylesheet by creating a variable with a temporary result and then applying the next step to the contents of a variable. You might need to use modes on your templates however to distinguish different transformation steps.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
Reply With Quote
  #7 (permalink)  
Old March 2nd, 2010, 10:08 AM
Authorized User
 
Join Date: Feb 2009
Location: Gurgaon
Posts: 31
Thanks: 6
Thanked 1 Time in 1 Post
Send a message via MSN to anil_yadav26@hotmail.com
Smile

Could you please suggest some code or documentation on this.

Thanks,
Anil Yadav
Reply With Quote
  #8 (permalink)  
Old March 2nd, 2010, 11:34 AM
Friend of Wrox
Points: 6,676, Level: 34
Points: 6,676, Level: 34 Points: 6,676, Level: 34 Points: 6,676, Level: 34
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Nov 2007
Location: Germany
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

The approach looks like this, assuming you always want to transform the complete document:
Code:
<xsl:template match="/">
  <xsl:variable name="sorted">
     <xsl:apply-templates mode="sort"/>
  </xsl:variable>
  <xsl:variable name="unique">
     <xsl:apply-templates select="$sorted/node()" mode="unique"/>
  </xsl:variable>
  <xsl:apply-templates select="$unique/node()"/>
</xsl:template>

<!-- now put all templates for sorting in mode="sort" e.g.
       <xsl:template match="index-item" mode="sort">
       
       </xsl:template>
       and all templates to eliminate duplicates in mode="unique"
       and then simply insert the templates from the last stylesheet
       that creates the ranges
-->
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
Reply With Quote
  #9 (permalink)  
Old March 3rd, 2010, 04:46 AM
Authorized User
 
Join Date: Feb 2009
Location: Gurgaon
Posts: 31
Thanks: 6
Thanked 1 Time in 1 Post
Send a message via MSN to anil_yadav26@hotmail.com
Smile

Hi Martin,
Thanks a lot for your kind response!!!
You are genius.

Thanks,
Anil Yadav
Reply With Quote
Reply


Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off
Trackbacks are Off
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Index was out of range. Must be non-negative raghavendra_das ASP.NET 2.0 Professional 0 July 24th, 2008 08:16 AM
index out of range datagrid muchengeti ASP.NET 2.0 Professional 0 April 21st, 2006 12:43 PM
Index Out of range exception PbsiGuru General .NET 3 March 22nd, 2004 11:04 AM



All times are GMT -4. The time now is 07:14 PM.


Powered by vBulletin®
Copyright ©2000 - 2019, Jelsoft Enterprises Ltd.
© 2013 John Wiley & Sons, Inc.