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 January 31st, 2013, 07:21 AM
Registered User
Points: 15, Level: 1
Points: 15, Level: 1 Points: 15, Level: 1 Points: 15, Level: 1
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jan 2013
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
Default How to group involving tokenize using XSLT 1.0 without external function

General outline: I need to write a transformation restricted using XSLT 1.0 possibilties only without using any external extension. I took the basic sample files from Michael Kay's excellent XSLT: Programmer's Reference, 2nd Edition by downloading its sample codes .

!! I'd read the advise putting a specific question to the appropriate forum here at wrox, however my task is to extending this basic grouping sample with a tokenizing or splitting involving it. Because I think the problem may be more general (only the example shown is specific to the above mentioned book), I posted my question here.

Sample task: Let's suppose there are same city names in different countries ...

Here is the sample source cities.xml file being a little extended and also abbreviated

Code:
<cities>
   <city name="Paris" country="France,Texas"/>
   <city name="Venice" country="Italia,Luisiana"/>
   <city name="Roma" country="Italia"/>
   <city name="Nice" country="France"/>
   <city name="Milano" country="Italia"/>
   <city name="Firenze" country="Italia"/>
   <city name="Napoli" country="Italia"/>
   <city name="Lyon" country="France"/>
</cities>
I expect the following HTML output
HTML Code:
<h1>France</h1>
 Paris<br/>
 Nice<br/>
 Lyon<br/>
<h1>Texas</h1>
 Paris<br/>
</h1>Italia</h1>
 Venice<br/>
 Roma<br/>
 Milano<br/>
 Firenze<br/>
 Napoli<br/>
<h1>Luisiana</h1>
 Venice<br/>
here is my extended cities_group_and_tokenize.xsl transformation file trying to solve the problem

Code:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:key name="country-group" match="city" use="@country"/>

<xsl:template match="/">
    <html>
    <body>

<!-- Need to separate/split @country which contains country1, country2 without any extension used-->
        <xsl:variable name="countryitems" >
            <xsl:call-template name="splitcountries">
                <xsl:with-param name="string" select="//@country"/>
            </xsl:call-template>
        </xsl:variable>

<!-- I'm trying only to test it here how to refer to the key after splitting  -->
<xsl:variable name="testcountryitem" >
<xsl:value-of select="$countryitems/token/@country" />
</xsl:variable>

<xsl:for-each select="//city[generate-id() = generate-id(key('country-group', $testcountryitem)[1])]">

<!--  <xsl:for-each select="//city[generate-id() = generate-id(key('country-group', @country)[1])]">-->
        <h1><xsl:value-of select="@country"/></h1>
            <xsl:for-each select="key('country-group', @country)">
            <xsl:value-of select="@name"/><br/>
            </xsl:for-each>
</xsl:for-each>

    </body>
    </html>
</xsl:template>

<!-- Split countries subtemplate -->
<xsl:template name="splitcountries">
<xsl:param name="string"/>
<xsl:param name="separator" select="','"/>

<xsl:choose>
  <xsl:when test="contains($string,$separator)">
        <token country="{substring-before($string,$separator)}">
<!--     <xsl:value-of select="substring-before($string,$separator)"/> -->
    </token>
    <xsl:call-template name="splitcountries">
      <xsl:with-param name="string" select="substring-after($string,$separator)"/>
    </xsl:call-template>
  </xsl:when>
  <xsl:otherwise>
      <xsl:choose>
          <xsl:when test="string-length($string) = 0">
              <token country="Empty"></token>
          </xsl:when>
          <xsl:otherwise>
                <token country="{$string}">
<!--              <xsl:value-of select="$string"/> -->
                </token>
          </xsl:otherwise>
        </xsl:choose>
  </xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Split countries subtemplate -->

</xsl:transform>
My above transformation file outputs only

France
Nice
Lyon

then finishes its working.

----- Notes -----

I suppose, the problem may be

1. <xsl:key name="country-group" match="city" use="@country"/>

top level declaration will generate a key-table (see: behind the keys http://www.cranesoftwrights.com/reso...keys/index.htm) something like this

key-table
IDX key values
1 France,Texas
2 Italia,Luisiana
3 Italia
4 France
5 Italia
6 Italia
7 Italia
8 France

2. In the outer loop of the above given transformation file

<xsl:for-each select="//city[generate-id() = generate-id(key('country-group', $testcountryitem)[1])]">

will generate the id for the first token 'France' contained in $testcountryitem probably well

3. However in the second inner loop of it

<xsl:for-each select="key('country-group', @country)">

will go through the key-table and it will find only two matches (see idx=4 and idx=8 in the above key-table)

4. I guess, it doesn't find 'France' at idx=1 in the key-table, because there is not any instruction how to split even that key value too. And the 'France,Texas' string really doesn't same as 'France' string only.

5. Because I can't use any external EXSLT str:split, EXSLT str:tokenize, or Saxon saxon:tokenize functions in the top <xsl:key ... use=".." declaration which will make the key-values in the key_table supposedly splitting

5.1 I only guess, either I need to use an other top level

<xsl:key name="country-group" match="token" use="@country"/>
<xsl:key name="country-group" match="token" use="."/>


declaration(s) (using either of the versions concerning see: above the written 'Split countries subtemplate') for generating a union key-table which somehow will contain additional but the splitting key-values too.

However I don't understand generally whether there will be any key-table generated at all when a sub template is called? Factually the sub template will give a tree fragment tokens held in a variable (countryitems) which are not real nodes on which the key generation process will go through as per my present understanding. So I suppose my guessed declaration is useless.

5.2 Or, I can't guess even how to resolve this combined problem.

Grateful thanks, for giving me any advise or tip or comment.
  #2 (permalink)  
Old January 31st, 2013, 08:26 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

Does the XSLT 1.0 processor you use at least have an extension function like ms:node-set or exsl:node-set to transform a result tree fragment into a node-set for further processing? In that case I would suggest to write a two step transformation that in the first step splits up the comma separated country names into a result tree fragment and then in the second steps uses Muenchian grouping on the result tree fragment converted to a node-set.
If you don't have such an extension function then I would consider to write two stylesheets and chain them.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
  #3 (permalink)  
Old January 31st, 2013, 08:51 AM
Registered User
Points: 15, Level: 1
Points: 15, Level: 1 Points: 15, Level: 1 Points: 15, Level: 1
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jan 2013
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
Default

Hello Martin!

Thank you for your reply.

Unfortunately, my example files put it here are not the final ones which I need to resolve.
I wish to use the possible resolution of this globally meant question finally in a Firefox extension. Firefox uses a built-in processor and as per my knowledge Firefox allows using exsl:node-set. However, I did not try that. But, I've read before its trying on stackoverflow forum there are many problems about it.
This is why I looking for a resolution absolutely extension free.
Basically, I'd like testing and debugging the transformation with using Stylus Studio equipped with Saxon 9.1.x processor before taking any to try using it in Firefox. Saxon processor I think, allows using exsl:node-set.
But, I'd be grateful if you could give me tips for both resolutions.

I look forward for hearing from you and much thanks.

Last edited by iplnts; January 31st, 2013 at 08:54 AM..
  #4 (permalink)  
Old January 31st, 2013, 09:44 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

Firefox/Mozilla's XSLT processor has a rather good support of EXSLT by now, see https://developer.mozilla.org/en-US/docs/EXSLT#Strings. So I would use that if you know you target Firefox.

As for testing and debugging with Saxon 9.1, that is an XSLT 2.0 processor, I would certainly not use such a processor if I target an XSLT 1.0 processor. xsltproc is an XSLT 1.0 processor which also has good support of EXSLT which might be more suitable for debugging XSLT 1.0.

As for your sample, I tried
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="1.0"
  xmlns:exsl="http://exslt.org/common"
  xmlns:str="http://exslt.org/strings"
  exclude-result-prefixes="exsl str">

<xsl:output method="html" indent="yes"/>

<xsl:key name="by-country" match="countries/country" use="."/>

<xsl:key name="city-by-country" match="city" use="countries/country"/>

<xsl:variable name="cities-rtf">
  <cities>
    <xsl:apply-templates select="cities/city" mode="split"/>
  </cities>
</xsl:variable>

<xsl:template match="city" mode="split">
  <city name="{@name}">
    <countries>
       <xsl:for-each select="str:split(@country, ',')">
         <country>
           <xsl:value-of select="."/>
         </country>
       </xsl:for-each>
    </countries>
  </city>
</xsl:template>

<xsl:variable name="cities" select="exsl:node-set($cities-rtf)"/>

<xsl:template match="/">
  <div>
    <xsl:apply-templates select="$cities//country[generate-id() = generate-id(key('by-country', .)[1])]" mode="group"/>
  </div>
</xsl:template>

<xsl:template match="country" mode="group">
  <h1><xsl:value-of select="."/></h1>
  <ul>
    <xsl:apply-templates select="key('city-by-country', .)"/>
  </ul>
</xsl:template>

<xsl:template match="city">
  <li>
    <xsl:value-of select="@name"/>
  </li>
</xsl:template>

</xsl:stylesheet>
with the input
Code:
<?xml-stylesheet type="text/xsl" href="test2013013101.xsl"?>
<cities>
   <city name="Paris" country="France,Texas"/>
   <city name="Venice" country="Italia,Luisiana"/>
   <city name="Roma" country="Italia"/>
   <city name="Nice" country="France"/>
   <city name="Milano" country="Italia"/>
   <city name="Firenze" country="Italia"/>
   <city name="Napoli" country="Italia"/>
   <city name="Lyon" country="France"/>
</cities>
in a current version of Firefox and the result looks fine to me:
Code:
<div><h1>France</h1><ul><li>Paris</li><li>Nice</li><li>Lyon</li></ul><h1>Texas</h1><ul><li>Paris</li></ul><h1>Italia</h1><ul><li>Venice</li><li>Roma</li><li>Milano</li><li>Firenze</li><li>Napoli</li></ul><h1>Luisiana</h1><ul><li>Venice</li></ul></div>
__________________
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:
iplnts (January 31st, 2013)
  #5 (permalink)  
Old January 31st, 2013, 11:35 AM
Registered User
Points: 15, Level: 1
Points: 15, Level: 1 Points: 15, Level: 1 Points: 15, Level: 1
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jan 2013
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
Default

Hello Martin!

Much thanks for your code. I'll examine to try it adapting for my Firefox extension goals and run it under Firefox current version.

You point out to me, not using an appropriate xsltprocessor and software for debugging could cause me so much struggle. However Stylus Studio has only Saxon 9.1, and MSXMLx ones to select only by which it allows using its very good debugger capability. I don't have else software beside it for preliminary XSLT tests and debugging only VS 2010 equipped with also an XML/XSLT debugger capability (I tried that too), but the latter requires to install exslt for .NET.

Grateful thanks for your help.
  #6 (permalink)  
Old February 10th, 2013, 06:54 AM
Registered User
Points: 15, Level: 1
Points: 15, Level: 1 Points: 15, Level: 1 Points: 15, Level: 1
Activity: 0%
Activity: 0% Activity: 0% Activity: 0%
 
Join Date: Jan 2013
Posts: 4
Thanks: 1
Thanked 0 Times in 0 Posts
Default

Hello Martin!

I had the opportunity to try your code running on Firefox. It worked fine!
(I need yet to adopt it for my goals.)

Grateful thanks again for your help giving me the idea of the possible resolution.


Similar Threads
Thread Thread Starter Forum Replies Last Post
Tokenize in XSLT 1.0 using for-each for tokens chilly XSLT 0 April 20th, 2010 08:13 PM
Need Help in EXSLT function str:tokenize priby XSLT 2 September 1st, 2009 01:43 AM
tokenize function use required - please help jamesdurham XSLT 5 April 20th, 2009 11:56 AM
Urgent:How to call the external function ivanlaw Javascript 6 October 16th, 2007 12:16 AM
executing external command from XSLT kapy_kal XSLT 4 June 9th, 2006 11:26 AM





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