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 November 10th, 2005, 02:07 PM
Authorized User
 
Join Date: Mar 2004
Posts: 20
Thanks: 0
Thanked 0 Times in 0 Posts
Default Processing first node in a sequence

Hello,

I am trying to process the first node in a sequence of like nodes differently than the others (somewhat). Here is a sample and I'll explain.
Source doc:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <body>
        <heading>heading text</heading>
        <bodytext>bodytext1</bodytext>
        <bodytext>bodytext2</bodytext>
        <bodytext>bodytext3</bodytext>
        <bodytext>bodytext4</bodytext>
    </body>
</root>
and my attempt at the stylesheet
Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="root">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="heading">
        <Para FirstLineIndent="7">
            <xsl:value-of select="."/>
            <xsl:apply-templates select="bodytext[1]"/>
        </Para>
    </xsl:template>

    <xsl:template match="bodytext">
        <xsl:choose>
            <xsl:when test="../bodytext[position()=1]">        
                <HardSp/><HardSp/>
                    <xsl:apply-templates select="text()"/>
            </xsl:when>
            <xsl:otherwise>
                <Para FirstLineIndent="7">
                    <xsl:apply-templates/>
                </Para>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>
What I am trying to do is bring the first line of bodytext up into the same paragraph with the heading. All other bodytext elements should be in their own paragraphs. Such as:

<Para FirstLineIndent=7>heading text bodytext1</Para>
<Para FirstLineIndent=7>bodytext2</Para>
<Para FirstLineIndent=7>bodytext3</Para>
<Para FirstLineIndent=7>bodytext4</Para>

I have tried using the position() function to check if the bodytext element is the first in the series, but cannot seem to get this to work. Some of the errors I see are for un-matched pairs of Para tags, etc.

Are there any suggestions as to how to accomplish this?

Thanks in advance,
Dan


 
Old November 10th, 2005, 06:53 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

You are testing whether the parent of the current element has a bodytext element whose position is one.

You want

test="not(preceding-sibling::bodytext)"

Better still, write one template rule with match="bodytext[1]" and another with match="bodytext".

Michael Kay
http://www.saxonica.com/
Author, XSLT Programmer's Reference and XPath 2.0 Programmer's Reference
 
Old November 10th, 2005, 11:08 PM
Authorized User
 
Join Date: Mar 2004
Posts: 20
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Michael,

Thank you, that got me half-way there. I still have a problem in that after I execute the first "bodytext" element, the built-in 'apply-templates' fires and processes the remaining "bodytext" elements. This first "bodytext" element also needs to fire within the "Para" tags, currently it is writing its output after the "/Para" from the heading rule.

11/14 - latest xslt is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="root">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="heading">
        <Para FirstLineIndent="7">
            <xsl:value-of select="."/>


        </Para>
    </xsl:template>

    <xsl:template match="bodytext[1]">
        <HardSp/><HardSp/>
            <xsl:apply-templates select="."/>
    </xsl:template>

    <xsl:template match="bodytext">
        <Para FirstLineIndent="7">
            <xsl:apply-templates/>
        </Para>
    </xsl:template>
</xsl:stylesheet>


Any thoughts?

Dan
 
Old November 15th, 2005, 01:05 PM
Authorized User
 
Join Date: Mar 2004
Posts: 20
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Alright, I have found "the error of my ways" and will post so that others may avoid my mistake, unless I am the only one to do this.

When calling a specific template rule from within another, I had neglected to fully qualify the XPath location of the node to process (didn't include the //), instead only listing its name in the "Select" attribute. This resulted in the rule looking for a child node with that name, and I was trying to process a sibling. Hence it wasn't being fired when I wanted it to. Also, once corrected this first "bodytext" node was fired again via the built-in "Apply-Templates" rule. To avoid this I used a sibling test, to only process those "bodytext" nodes after the first one. So I combined some of my trial and error with Michael's reply from above.

The final XSLT is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

    <xsl:template match="root">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="heading">
        <Para FirstLineIndent="7">
            <xsl:value-of select="."/>
            <xsl:apply-templates select="//bodytext[1]" mode="header"/>
        </Para>
    </xsl:template>

    <xsl:template match="bodytext[1]" mode="header">
        <HardSp/><HardSp/>
        <xsl:value-of select="."/>
    </xsl:template>

    <xsl:template match="bodytext">
        <xsl:if test="preceding-sibling::bodytext">
            <Para FirstLineIndent="7">
                <xsl:value-of select="."/>
            </Para>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>
Best regards to all,
Dan






Similar Threads
Thread Thread Starter Forum Replies Last Post
The reference node is not a child of this node.XSL XMLUser XSLT 2 February 25th, 2008 05:22 AM
how to append child node after an node in XML + C# vishnu108mishra XML 5 November 13th, 2007 05:30 AM
Missing Node (Node data) pashworth XSLT 3 January 29th, 2007 12:39 PM
Node Processing adamflynn XSLT 6 September 7th, 2006 05:42 AM
Copying Source Node attributes to output node pvsat XSLT 2 November 3rd, 2005 09:46 AM





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