Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
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
 
Old April 12th, 2006, 08:31 PM
Registered User
 
Join Date: Apr 2006
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default <xsl:for-each> and key() - help needed

Hello,

I've been reading and trying the second day now, but I really can't figure out how to do this apparently simple thing. Maybe someone of you could please give me a hand with this...

My xml documents contain book-like content which is structured by sections and sub-sections, similar to DocBook. These documents contain data for different groups of readers, while some people (Group A) get to see the whole text and others (Group B) only a subset.

A simplified document would look like this (the visible-attribute defines whether the section is shown to Group B or not):

<?xml version='1.0' encoding='utf-8'?>
<?xml-stylesheet href='myStyles.xsl' type='text/xsl'?>
<Chapter>
<Title>My chapter</Title>
<Sect1 visible="True">
<Title>My section</Title>
<Sect2 visible="True">
     <Title>My first sub-section</Title>
</Sect2>
<Sect2 visible="False">
     <Title>My second sub-section</Title>
</Sect2>
<Sect2 visible="True">
     <Title>My third sub-section</Title>
</Sect2>
</Sect1>
<Sect1 visible="True">
<Title>My second section</Title>
<Sect2 visible="True">
     <Title>Another sub-section</Title>
</Sect2>
</Sect1>
</Chapter>


So far no problem, but now the sections have to be numbered automatically and continuously for both groups. The result for Group B should look like this:

1. My section
1.1 My first sub-section
1.2 My third sub-section
2. My second section
2.1 Another sub-section


The normal way of auto-numbering in xslt with

<xsl:number level="multiple" count="Sect1" format="1." />

doesn't work, because it still counts the invisible sections that it shouldn't. The result would be:

1. My section
1.1 My first sub-section
1.3 My third sub-section
2. My second section
2.1 Another sub-section


What I tried now is a solution with <xsl:for-each> selecting a key and then using the position() function. This works fine for the numbers, but now I get a problem with the sub-sections.

Instead of selecting just the visible sub-sections of each section, in each section all the documents visible sub-sections are selected and the result looks like this:

1. My section
1.1 My first sub-section
1.2 My third sub-section
1.3 Another sub-section
2. My second section
2.1 My first sub-section
2.2 My third sub-section
2.3 Another sub-section


This is the xslt code:

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

<xsl:key name="myKey1" match="Sect1" use="@visible" />
<xsl:key name="myKey2" match="Sect2" use="@visible" />

    <xsl:template match="/">
        <HTML>
            <HEAD>
                <TITLE><xsl:value-of select="./Chapter/Title" /></TITLE>
            </HEAD>
            <BODY>
                <xsl:apply-templates select="//Chapter"/>
            </BODY>
        </HTML>
    </xsl:template>

    <xsl:template match= "Chapter">
            <xsl:for-each select = "key('myKey1','True')" >
                 <xsl:apply-templates select=".">
                    <xsl:with-param name="CounterSect1p" select="position()"/>
                </xsl:apply-templates>
             </xsl:for-each>
    </xsl:template>

    <xsl:template match= "Sect1">
        <xsl:param name="CounterSect1p"/>
        <p><b>
            <xsl:number value="$CounterSect1p" format="1. " />
            <xsl:value-of select="Title"/>
        </b></p>

         <xsl:for-each select = "key('myKey2','True')">
             <xsl:apply-templates select=".">
                <xsl:with-param name="CounterSect1p" select="$CounterSect1p"/>
                <xsl:with-param name="CounterSect2p" select="position()"/>
            </xsl:apply-templates>
        </xsl:for-each>
    </xsl:template>

    <xsl:template match= "Sect2">
        <xsl:param name="CounterSect1p"/>
        <xsl:param name="CounterSect2p"/>
        <p>
            <xsl:number value="$CounterSect1p" format="1." />
            <xsl:number value="$CounterSect2p" format="1 " />
            <xsl:value-of select="Title"/>
        </p>
    </xsl:template>

</xsl:stylesheet>


I suppose the problem lies somewhere with the key() function in:

<xsl:for-each select = "key('myKey2','True')">

Could anyone please give me a hint how to apply the key() function only to child elements of the element I'm in? Or is the problem somewhere else?

Thank you,

Thomas
 
Old April 13th, 2006, 02:41 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

Have you tried

<xsl:number level="multiple" count="Sect1[@visible='True']"
   format="1." />

?

key() selects nodes that match the key anywhere in the document. They're not very useful if you simply want to filter the children of the context node: it's better in that case to use a predicate.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
 
Old April 13th, 2006, 04:36 AM
Registered User
 
Join Date: Apr 2006
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Thank you very much for your answer, works fine! Unfortunately there are more than two user groups and I created a visible-attribute for each group.

<Sect2 groupA="True" groupB="False" groupC="True">

This means that '@visible' in the following line would have to be replaced by the attribute-name of the group whose content has to be shown.

<xsl:number level="multiple" count="Sect1[@visible='True']" format="1." />

It would be nice to define the group once in a variable, but there is no way to use a variable instead of '@visible' . Or is there a workaround to achieve something like this: Sect1[$myGroup='True'] ?

The key() function solution in my previous post allows something similar to setting a variable by changing the use-attribute of the key-element.

<xsl:key name="myKey2" match="Sect2" use="@groupA" /> or
<xsl:key name="myKey2" match="Sect2" use="@groupB" /> and so on..

What do you reckon, is there a way out of here or is it the wrong way to define the visibility with attributes?

Thomas
 
Old April 13th, 2006, 05:12 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

I think your design is not a good one, because you will have to introduce new attributes with new names every time a new user group is invented. I think user groups are at the "instance" level rather than the "type" level, so their names should appear as values rather than names. That means coding it as

<section>
  <visibility group="A" visible="true"/>
  <visibility group="B" visible="false"/>

It's possible to process multiple attributes with related names using things like

section[@*[name()=$group and .='True']]

but it's a pretty messy approach.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
 
Old April 13th, 2006, 05:13 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

Incidentally, getting back to the section numbering question: I think this is an ideal candidate for a multiphase (pipelined) transformation. First create the subset of the document that the user is allowed to see, then apply rendition (e.g numbering) to that subset.

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
 
Old April 13th, 2006, 09:28 AM
Registered User
 
Join Date: Apr 2006
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Ok, thanks for your help and advise!





Similar Threads
Thread Thread Starter Forum Replies Last Post
Global variable restrictions in the <xsl:key> iceandrews XSLT 2 August 25th, 2008 01:16 PM
embedded <xsl:element> into <xsl:with-param> petergoodman XSLT 2 July 9th, 2008 06:36 AM
Performance for <xsl:import> and <xsl:include> vikkiefd XSLT 2 April 16th, 2008 08:06 AM
Using key() and <xsl:key> freddy XSLT 2 January 18th, 2007 08:55 PM
<xsl:key> prints extra string? di98svpa XSLT 6 February 6th, 2006 12:13 PM





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