Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
| 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 software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
  #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..
  #2 (permalink)  
Old March 2nd, 2010, 08:12 AM
mhkay's Avatar
Wrox Author
Points: 18,487, Level: 59
Points: 18,487, Level: 59 Points: 18,487, Level: 59 Points: 18,487, Level: 59
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Apr 2004
Location: Reading, Berks, United Kingdom.
Posts: 4,962
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
  #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
  #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
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>
  #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
  #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
  #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
  #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


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





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.