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 December 2nd, 2009, 01:00 AM
Authorized User
 
Join Date: Nov 2007
Posts: 33
Thanks: 2
Thanked 0 Times in 0 Posts
Question multiple xslt help

Hi,

I have a multiple xslt process.

I would like to divide a group, for example the party that start between 20:00:00 until 24:00:00 will call as "Group B" and I would like to record the "number" where is start and end the party_group element can be anywhere, doesn't have to be at the beginning.

Will this be possible to do? I'm confused how to keep the start and end number. I hope someone can give me some tips. Thanks so much

Here is my input
Code:
<parties>
    <events>
        <event id="a">
            <type>dance party</type>
            <title>80s</title>
            <start>2009-12-01T00:28:30</start>
        </event>
        <event id="b">
            <type>cocktail party</type>
            <title>Flower Garden</title>
            <start>2009-12-01T13:09:34</start>
        </event>
        <event id="c">
            <type>cocktail party</type>
            <title>Prewedding party</title>
            <start>2009-12-01T13:30:34</start>
        </event>
        <event id="d">
            <type>kids party</type>
            <title>Fairy Party</title>
            <start>2009-12-01T20:00:00</start>
        </event>
        <event id="e">
            <type>kids party</type>
            <title>Animals Party</title>
            <start>2009-12-01T20:05:00</start>
        </event>
    </events>
</parties>
I have a two-pass process xslt because I would like to have number to the output look like this as the output
Code:
<mypartiescollection Version="1.1">
    <dance_party number="1" id="a">
        <title>80s</title>
    </dance_party>
    <cocktail_party number="2" id="b">
        <title>Flower Garden</title>
    </cocktail_party>
    <cocktail_party number="3" id="c">
        <title>Prewedding party</title>
    </cocktail_party>
    <kids_party number="4" id="d">
        <title>Fairy Party</title>
    </kids_party>
    <kids_party number="5" id="e">
        <title>Animals Party</title>
    </kids_party>
</mypartiescollection>
Here is my xslt test.xsl
Code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/" mode="phase1">
        <xsl:element name="mypartiescollection">
            <xsl:attribute name="Version">1.1</xsl:attribute>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    
    <xsl:template match="event" name="hello">
        <xsl:variable name="hours" select="format-number(fn:hours-from-dateTime(xs:dateTime(start)),'00')"/>
        <xsl:variable name="minutes" select="format-number(fn:minutes-from-dateTime(xs:dateTime(start)),'00')"/>
        <xsl:variable name="seconds" select="format-number(fn:seconds-from-dateTime(xs:dateTime(start)),'00')"/>
        <xsl:variable name="eventTime" select="concat($hours,':',$minutes,':',$seconds)"/>
                        
        <xsl:if test="xs:time($eventTime) ge xs:time('06:00:00')">
            <xsl:element name="party_group">
                <xsl:attribute name="name">Group A</xsl:attribute>
                <xsl:attribute name="fromNumber"><!-- start number range for example --></xsl:attribute>
                <xsl:attribute name="toNumber"><!-- end number range for example 5--></xsl:attribute>
          
            </xsl:element>
        </xsl:if>
        
        <xsl:if test="xs:time($eventTime) ge xs:time('20:00:00')">
            <xsl:element name="party_group">
                <xsl:attribute name="name">Group B</xsl:attribute>
                <xsl:attribute name="fromNumber"><!-- start number range for example --></xsl:attribute>
                <xsl:attribute name="toNumber"><!-- end number range for example 5--></xsl:attribute>
         
            </xsl:element>
        </xsl:if>
    
        <xsl:if test="type='dance party'">
            <xsl:element name="dance_party">
                <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select="title"/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
        <xsl:if test="type = 'cocktail party'">
            <xsl:element name="cocktail_party">
                <xsl:attribute name="id"><xsl:value-of select='@id'></xsl:value-of></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select='title'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
        <xsl:if test="type = 'kids party'">
            <xsl:element name="kids_party">
                <xsl:attribute name="id"><xsl:value-of select='@id'></xsl:value-of></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select='title'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>
and second part xslt
Code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:import href="test.xslt"/>
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:variable name="data">
        <xsl:apply-templates select="/" mode="phase1"/>
    </xsl:variable>
    
    <xsl:template match="/">
        <xsl:apply-templates select="$data" mode="phase2"/>
    </xsl:template>
    
    <xsl:template match="mypartiescollection" mode="phase2">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates select="node()" mode="phase2"/>
    </xsl:copy>
    </xsl:template>
    
 <xsl:template match="dance_party|cocktail_party|kids_party" mode="phase2" name="party_collection">
  <xsl:copy>
    <xsl:attribute name="number">
       <xsl:number count="dance_party|cocktail_party|kids_party"/>
    </xsl:attribute>
    <xsl:copy-of select="@* | node()"/>
  </xsl:copy>
</xsl:template>
</xsl:stylesheet>
So the output will be like this
Code:
<mypartiescollection Version="1.1">
   <party_group name="Group A" fromNumber="1" toNumber="3"/>
   <party_group name="Group B" fromNumber="4" toNumber="5"/>
    <dance_party number="1" id="a">
        <title>80s</title>
    </dance_party>
    <cocktail_party number="2" id="b">
        <title>Flower Garden</title>
    </cocktail_party>
    <cocktail_party number="3" id="c">
        <title>Prewedding party</title>
    </cocktail_party>
    <kids_party number="4" id="d">
        <title>Fairy Party</title>
    </kids_party>
    <kids_party number="5" id="e">
        <title>Animals Party</title>
    </kids_party>
</mypartiescollection>
 
Old December 2nd, 2009, 05:07 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

The following stylesheet:

Code:
<xsl:stylesheet version="2.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:xs="http://www.w3.org/2001/XMLSchema"
  xmlns:f="http://local/functions"
  exclude-result-prefixes="xs f">
  
    <xsl:strip-space elements="*"/>
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:function name="f:party-group" as="xs:string">
       <xsl:param name="start-time" as="xs:time"/>
       <xsl:sequence select="if ($start-time gt xs:time('20:00:00')) then 'Group B' else 'Group A'"/>
    </xsl:function>
    
    <xsl:function name="f:party-number" as="xs:integer">
       <xsl:param name="event" as="element(event)"/>
       <xsl:number select="$event"/>
    </xsl:function>
    
    <xsl:template match="/">
        <mypartiescollection Version="1.1">
           <xsl:for-each-group select="//event" group-by="f:party-group(xs:time(xs:dateTime(start)))">
              <party_group name="{current-grouping-key()}"
                       fromNumber="{min(current-group()/f:party-number(.))}"
                       toNumber="{max(current-group()/f:party-number(.))}"
                       />
           </xsl:for-each-group>
           <xsl:apply-templates/>
        </mypartiescollection>
    </xsl:template>
    
    <xsl:template match="event">
       
       <xsl:element name="{translate(type, ' ', '_')}">
          <xsl:copy-of select="@id"/>
          <xsl:attribute name="number" select="f:party-number(.)"/>
          <xsl:copy-of select="title"/>
       </xsl:element>
    </xsl:template>
    

</xsl:stylesheet>
produces this output, which seems to be roughly what you are asking for though it does not match your specimen output exactly:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<mypartiescollection Version="1.1">
   <party_group name="Group A" fromNumber="1" toNumber="4"/>
   <party_group name="Group B" fromNumber="5" toNumber="5"/>
   <dance_party id="a" number="1">
      <title>80s</title>
   </dance_party>
   <cocktail_party id="b" number="2">
      <title>Flower Garden</title>
   </cocktail_party>
   <cocktail_party id="c" number="3">
      <title>Prewedding party</title>
   </cocktail_party>
   <kids_party id="d" number="4">
      <title>Fairy Party</title>
   </kids_party>
   <kids_party id="e" number="5">
      <title>Animals Party</title>
   </kids_party>
</mypartiescollection>
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference

Last edited by mhkay; December 2nd, 2009 at 05:10 AM..
 
Old December 7th, 2009, 07:34 PM
Authorized User
 
Join Date: Nov 2007
Posts: 33
Thanks: 2
Thanked 0 Times in 0 Posts
Unhappy drawing time limit

Thanks Mr Kay, I really appreciate your help.

I've been trying to reassamble your xslt with my xslt for days, but I still have no luck.
The reason that I have 2 pass xslt is related to how to increment global variable

This is what I have so far.

Code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:output method="xml" indent="yes"/>
    <xsl:template match="/" mode="phase1">
        <xsl:element name="mypartiescollection">
            <xsl:attribute name="Version">1.1</xsl:attribute>
            <xsl:apply-templates/>
        </xsl:element>
    </xsl:template>
    
    <xsl:template match="event">
        <xsl:if test="type='dance party'">
            <xsl:element name="dance_party">
                <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select="title"/>
                </xsl:element>
                <xsl:element name="start">
                    <xsl:value-of select='start'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
        <xsl:if test="type = 'cocktail party'">
            <xsl:element name="cocktail_party">
                <xsl:attribute name="id"><xsl:value-of select='@id'></xsl:value-of></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select='title'/>
                </xsl:element>
                <xsl:element name="start">
                    <xsl:value-of select='start'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
        <xsl:if test="type = 'kids party'">
            <xsl:element name="kids_party">
                <xsl:attribute name="id"><xsl:value-of select='@id'></xsl:value-of></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select='title'/>
                </xsl:element>     
                <xsl:element name="start">
                    <xsl:value-of select='start'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>
phase1 result:
Code:
<mypartiescollection Version="1.1">
    <dance_party number="1" id="a">
        <title>80s</title>
        <start>2009-12-01T00:28:30</start>
    </dance_party>
    <cocktail_party number="2" id="b">
        <title>Flower Garden</title>
        <start>2009-12-01T13:09:34</start>
    </cocktail_party>
    <cocktail_party number="3" id="c">
        <title>Prewedding party</title>
        <start>2009-12-01T13:30:34</start>
    </cocktail_party>
    <kids_party number="4" id="d">
        <title>Fairy Party</title>
        <start>2009-12-01T20:00:00</start>
   </kids_party>
    <kids_party number="5" id="e">
        <title>Animals Party</title> 
        <start>2009-12-01T20:05:00</start>
    </kids_party>
</mypartiescollection>
I was trying to create a temporary tree as the above result then I group it according to party-group function then get the min and max number of that group.

Code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:import href="test.xslt"/>
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:variable name="data">
        <xsl:apply-templates select="/" mode="phase1"/>
    </xsl:variable>
    
    <xsl:template match="/">
        <xsl:variable name="data2">
        <xsl:apply-templates select="$data" mode="phase2"/>
    </xsl:variable>        
        <xsl:apply-templates select="$data2" mode="group"/>
    </xsl:template>

    <xsl:template match="/" mode="group">
        <xsl:for-each-group select="//event" group-by="f:party-group(xs:time(xs:dateTime(start)))"> 

Code:
somehow this part have error of "An empty sequence is not allowed as the first argument of f:party_group()
<party_group name="{current-grouping-key()}" fromNumber="{min(current-group()/number)}" toNumber="{max(current-group()/number)}" /> </xsl:for-each-group> </xsl:template> <xsl:function name="f:party-group" as="xs:string"> <xsl:param name="start-time" as="xs:time"/> <xsl:sequence select="if ($start-time gt xs:time('20:00:00')) then 'Group B' else 'Group A'"/> </xsl:function> <xsl:template match="mypartiescollection" mode="phase2"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates select="node()" mode="phase2"/> </xsl:copy> </xsl:template> <xsl:template match="dance_party|cocktail_party|kids_party" mode="phase2" name="party_collection"> <xsl:copy> <xsl:attribute name="number"> <xsl:number count="dance_party|cocktail_party|kids_party"/> </xsl:attribute> <xsl:copy-of select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
Please excuse my lack of understanding, but why does "start" is empty when I pass for party-group in "f:party-group(xs:time(xs:dateTime(start)))"?

Thanks,
 
Old December 8th, 2009, 08:07 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

I can't understand why you are making this so complicated. You haven't explained why the much simpler solution I gave you doesn't work. I'm afraid it leaves me very confused.

In your code, you actually seem to be doing three phases of processing! The for-each-group in the third phase is processing the tree in $data2, I can't see how this comes to have any <event> elements in it - but from the error you are getting, it must have. But I'm reluctant to start debugging it for you, because I really can't see why you are writing it this way.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old December 9th, 2009, 11:32 AM
Authorized User
 
Join Date: Nov 2007
Posts: 33
Thanks: 2
Thanked 0 Times in 0 Posts
Question drawing time limit

I'm sorry, I probably didn't explain properly.

The "number" attribute is not an event number position. For example, if the type of "kids party" then I want to create another element "comment", just like my posting in how to increment global variable

That's why I need this 2 process to get the "number" that numbering all the element that I've been created in phase 1.

So I was thinking to create another variable including the "start" element, therefore I can group by "start" date after I created the "number" then remove "start" element to get the final result. Not sure how to remove the element though

phase1
Code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
        <xsl:output method="xml" indent="yes"/>
        <xsl:template match="/" mode="phase1">
                <xsl:element name="mypartiescollection">
                       <xsl:attribute name="Version">1.1</xsl:attribute>
           <xsl:apply-templates/>
                </xsl:element>
    </xsl:template>
    
        <xsl:template match="event">
       <xsl:if test="type='dance party'">
          <xsl:element name="dance_party">
          <xsl:attribute name="id"><xsl:value-of select="@id"/></xsl:attribute>
                          <xsl:element name="title">
               <xsl:value-of select="title"/>
                          </xsl:element>
                <xsl:element name="start">
                    <xsl:value-of select='start'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
        <xsl:if test="type = 'cocktail party'">
            <xsl:element name="cocktail_party">
                <xsl:attribute name="id"><xsl:value-of select='@id'></xsl:value-of></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select='title'/>
                </xsl:element>
                <xsl:element name="start">
                    <xsl:value-of select='start'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
        <xsl:if test="type = 'kids party'">
<xsl:element name="comment>
     <xsl:attribute name="message"/>my comment</xsl:attribute>
</xsl:element>
            <xsl:element name="kids_party">
                <xsl:attribute name="id"><xsl:value-of select='@id'></xsl:value-of></xsl:attribute>
                <xsl:element name="title">
                    <xsl:value-of select='title'/>
                </xsl:element>     
                <xsl:element name="start">
                    <xsl:value-of select='start'/>
                </xsl:element>
            </xsl:element>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>
my phase 1 result before having "number" attribute
Code:
<mypartiescollection Version="1.1">
    <dance_party id="a">
        <title>80s</title>
        <start>2009-12-01T00:28:30</start>
    </dance_party>
    <cocktail_party id="b">
        <title>Flower Garden</title>
        <start>2009-12-01T13:09:34</start>
    </cocktail_party>
    <cocktail_party id="c">
        <title>Prewedding party</title>
        <start>2009-12-01T13:30:34</start>
    </cocktail_party>
    <comment message="my comment" />
    <kids_party number="5" id="d">
        <title>Fairy Party</title>
        <start>2009-12-01T20:00:00</start>
   </kids_party>
   <comment message="my comment"/>
    <kids_party number="7" id="e">
        <title>Animals Party</title> 
        <start>2009-12-01T20:05:00</start>
    </kids_party>
</mypartiescollection>
Code:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
    <xsl:import href="test.xslt"/>
    <xsl:output method="xml" indent="yes"/>
    
    <xsl:variable name="data">
        <xsl:apply-templates select="/" mode="phase1"/>
    </xsl:variable>
    
    <xsl:template match="/">
        <xsl:variable name="data2">
        <xsl:apply-templates select="$data" mode="phase2"/>
    </xsl:variable>        
        <xsl:apply-templates select="$data2" mode="group"/>
    </xsl:template>

    <xsl:template match="/" mode="group">
        <xsl:for-each-group select="//dance_party|//cocktail_party|//kids_party" group-by="f:party-group(xs:time(xs:dateTime(start)))"> 

    
Code:
I'm not so sure about this part. somehow this part have error of "An empty sequence is not allowed as the first argument of f:party_group()
<party_group name="{current-grouping-key()}" fromNumber="{min(current-group()/number)}" toNumber="{max(current-group()/number)}" /> </xsl:for-each-group> </xsl:template> <xsl:function name="f:party-group" as="xs:string"> <xsl:param name="start-time" as="xs:time"/> <xsl:sequence select="if ($start-time gt xs:time('20:00:00')) then 'Group B' else 'Group A'"/> </xsl:function> <xsl:template match="mypartiescollection" mode="phase2"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates select="node()" mode="phase2"/> </xsl:copy> </xsl:template> <xsl:template match="dance_party|cocktail_party|kids_party" mode="phase2" name="party_collection"> <xsl:copy> <xsl:attribute name="number"> <xsl:number count="dance_party|cocktail_party|kids_party"/> </xsl:attribute> <xsl:copy-of select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
$data2 result:
Code:
<mypartiescollection Version="1.1">
    <dance_party number="1" id="a">
        <title>80s</title>
        <start>2009-12-01T00:28:30</start>
    </dance_party>
    <cocktail_party number="2" id="b">
        <title>Flower Garden</title>
        <start>2009-12-01T13:09:34</start>
    </cocktail_party>
    <cocktail_party number="3" id="c">
        <title>Prewedding party</title>
        <start>2009-12-01T13:30:34</start>
    </cocktail_party>
    <comment message="my comment" number="4" />
    <kids_party number="5" id="d">
        <title>Fairy Party</title>
        <start>2009-12-01T20:00:00</start>
   </kids_party>
   <comment message="my comment" number="6"/>
    <kids_party number="7" id="e">
        <title>Animals Party</title> 
        <start>2009-12-01T20:05:00</start>
    </kids_party>
</mypartiescollection>
final result:
Code:
<mypartiescollection Version="1.1">
   <party_group name="Group A" fromNumber="1" toNumber="3"/>
   <party_group name="Group B" fromNumber="4" toNumber="6"/>
    <dance_party number="1" id="a">
        <title>80s</title>
    </dance_party>
    <cocktail_party number="2" id="b">
        <title>Flower Garden</title>
    </cocktail_party>
    <cocktail_party number="3" id="c">
        <title>Prewedding party</title>
    </cocktail_party>
    <comment message="my comment" number="4"/>
    <kids_party number="5" id="d">
        <title>Fairy Party</title>
    </kids_party>
<comment message="my comment" number="5"/>
    <kids_party number="6" id="e">
        <title>Animals Party</title>
    </kids_party>
</mypartiescollection>

Thanks so much.
 
Old December 11th, 2009, 12:01 AM
Authorized User
 
Join Date: Nov 2007
Posts: 33
Thanks: 2
Thanked 0 Times in 0 Posts
Default

It's impossible isn't it? :(
 
Old December 11th, 2009, 05:32 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

I don't think it's at all impossible. I just think you're making it difficult by not stating the requirements clearly. I'm afraid that when I answer a simple question by giving a simple piece of code, and people then come back and say actually, the problem is not what they said, then I tend to lose interest.
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference





Similar Threads
Thread Thread Starter Forum Replies Last Post
parse multiple .XSD files with XSLT chaostimmy XSLT 12 August 17th, 2009 12:06 PM
XSLT Code for Multiple occurrences vijayp2p XSLT 1 May 9th, 2006 11:23 AM
XSLT to make multiple hyperlinked HTML files dai.hop XSLT 1 January 11th, 2006 03:05 PM
Multiple XML Docs/One XSLT Stylesheet kwilliams XSLT 5 August 4th, 2005 11:54 AM
xslt with multiple occurrences in a record Olaf_l XSLT 2 April 7th, 2005 07:02 AM





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