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 March 16th, 2009, 02:13 PM
Registered User
 
Join Date: Mar 2009
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default How to select a branch of an xml file if it contains a node with a certain attribute?

Straight to the point:
It might make this a lot simpler for people to help me if I just say it like this:

I need to
-select all nodes in a branch of the xml file
-where one of the nodes in that branch
-contains a node with an attribute "navigateurl" that is equal to $RequestedPage

But if not please read on for my longer explanation of my problem!


I am using AspDotNetStorefront which makes use of XSLT transformations to do various things. I am having a problem with making a submenu that only shows sections of an xml file.

I have an xml menu that has <page> elements nested inside each other to form a sitemap style tree.

I have built an xslt that will recursively traverse the xml and transform this into a ul list.

The storefront also injects a separate node that contains the url of the currently requested page.

Question
--------------------------------------------
When I compare the <page> element to the current page variable I only want to show the <page> elements from that section of the tree. So if its a page halfway down the "about us" section of the tree I only want to show "about us" pages.

Please take a look at my xslt and tell me how I can pass the correct node-set to my template so that I only have one "branch" of the tree which is whichever branch the current page is in.

Example:

For this tree layout (presume that terms.aspx is the current page):
--home.aspx
--aboutus.aspx
---privacypolicy.aspx
---terms.aspx
--anotherunwantedsection.aspx
---unwantedpage.aspx
---unwantedpage2.aspx

How do I only get the about us branch?

--------------------------------------------

My XML File:
Note - the xml file is loaded into a larger set so that's why my xslt references this file as /root/MenuData/menu instead of /menu
HTML Code:
<?xml version="1.0" encoding="utf-8"?>
<menu>
  <!-- NOTE - Do not prefix urls with / -->
  <page navigateurl="t-about.aspx" anchortext="About Us">
      <page navigateurl="t-about-returns-policy.aspx" anchortext="Return Policy" />
    <page navigateurl="t-about-privacy-policy.aspx" anchortext="Privacy Policy" />
    <page navigateurl="t-about-terms-and-conditions.aspx" anchortext="Terms &amp; Conditions" />
    <page navigateurl="t-about-accessibility.aspx" anchortext="Accessibility" />
    <page navigateurl="t-about-payment-and-shipping.aspx" anchortext="Payment &amp; Shipping Options" />
    <page navigateurl="t-about-doctor-information.aspx" anchortext="Doctor Information" />
  </page>
  <page navigateurl="c-1-eye-support-products.aspx" anchortext="Eye Support Products">
      <page navigateurl="p-4-n-acetyl-carnosine.aspx" anchortext="N-Acetyl-Carnosine Eye Drops">
        <page navigateurl="t-can-c-testimonials.aspx" anchortext="Testimonials" />
        <page navigateurl="t-can-c-faqs.aspx" anchortext="Can-C FAQs" />
        <page navigateurl="t-can-c-about.aspx" anchortext="About Can-C">
            <page navigateurl="t-can-c-about-ingredients.aspx" anchortext="Can-C Ingredients &amp; Instructions" />
            <page navigateurl="t-can-c-about-how.aspx" anchortext="How and Why Can-C Reverses Cataracts" />
        </page>
        <page navigateurl="t-dr-babizhayev-interviews-and-more.aspx" anchortext="Dr Babizhayev Interviews &amp; More">
            <page navigateurl="t-dr-babizhayev-interview-2001.aspx" anchortext="Dr. Babizhayev interview 2001" />
            <page navigateurl="t-dr-babizhayev-interview-2003.aspx" anchortext="Dr. Babizhayev Interview 2003" />
            <page navigateurl="t-dr-babizhayev-warning.aspx" anchortext="Dr. Babizhayev Warning" />
            <page navigateurl="t-dr-babizhayev-biography.aspx" anchortext="Dr. Babizhayev Biography" />
            <page navigateurl="t-innovative-vision-products.aspx" anchortext="Innovative Vision Products - Company Overview" />
        </page>    
        <page navigateurl="t-can-c-science-and-research.aspx" anchortext="Science &amp; Research">
            <page navigateurl="t-clinical-publications.aspx" anchortext="Clinical Publications" />
            <page navigateurl="#" anchortext="Ocular Support" />
            <page navigateurl="#" anchortext="How and Why Can-C Reverses Cataracts" />
            <page navigateurl="t-can-c-dr-robert-mason-interview.aspx" anchortext="Dr. Robert Mason Interview" />
            <page navigateurl="t-can-c-cataract-surgery-risks.aspx" anchortext="Cataract Surgery Risks - Article" />
        </page>
      </page>
      <page navigateurl="p-6-can-c-plus.aspx" anchortext="Can-C Plus">
        <page navigateurl="#" anchortext="Testimonials" />
        <page navigateurl="#" anchortext="Science &amp; Research" />
      </page>
      <page navigateurl="p-5-can-c-for-dogs.aspx" anchortext="Can-C For Dogs">
        <page navigateurl="t-can-c-dogs-testimonials.aspx" anchortext="Testimonials" />
        <page navigateurl="#" anchortext="Science &amp; Research" />
      </page>
      <page navigateurl="p-7-macular-support-armd.aspx" anchortext="Macular Support (ARMD)">
        <page navigateurl="#" anchortext="Testimonials" />
        <page navigateurl="#" anchortext="Science &amp; Research" />
      </page>
  </page>
  <page navigateurl="t-eyeconditioninfo.aspx" anchortext="Eye Condition Info">
    <page navigateurl="#" anchortext="Degenerative Eye Disease" />
    <page navigateurl="#" anchortext="Cataracts" />
    <page navigateurl="#" anchortext="Dry Eye Condition" />
    <page navigateurl="#" anchortext="Macular Degeneration" />
    <page navigateurl="#" anchortext="Glaucoma" />
    <page navigateurl="#" anchortext="Presbyopia" />
    <page navigateurl="#" anchortext="Retinal Disease" />
    <page navigateurl="#" anchortext="Floaters (Vitreous Opacities)" />
    <page navigateurl="#" anchortext="Corneal Disorders" />
    <page navigateurl="#" anchortext="Ocular Inflamation" />
  </page>
</menu>
And my XSLT File:
HTML Code:
        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aspdnsf="urn:aspdnsf" exclude-result-prefixes="aspdnsf">
        <xsl:output method="html" omit-xml-declaration="yes" />

        <!-- note - path below should be relative -->        
        <!-- <xsl:variable name="Menu" select="document('/skins/Skin_1/subpagemenu.xml', /)/menu" /> -->
        <xsl:variable name="RequestedPage" select="/root/System/RequestedPage" />
        
        <xsl:template match="/">
            <xsl:element name="ul">
                <xsl:attribute name="id">LeftNav</xsl:attribute>
<!--                <xsl:apply-templates select="/root/MenuData/menu/page[@navigateurl=$RequestedPage]/ancestor::root/MenuData/menu/page" />-->
<!--                <xsl:apply-templates select="/root/MenuData/menu/page" /> -->
                    <xsl:apply-templates select="descendant::root/MenuData/menu/page[@navigateurl=$RequestedPage]" />
            </xsl:element>
        </xsl:template>
        
        <xsl:template match="page">
            <li>
                <!-- current page highlight test: current page or ancestor of current page -->
                <xsl:if test="@navigateurl = $RequestedPage or count(descendant::page[attribute::navigateurl=$RequestedPage]) &gt; 0"> 
                    <xsl:attribute name="class">active</xsl:attribute>
                </xsl:if>

                <a href="/{@navigateurl}">
                    <xsl:value-of select="@anchortext"/>
                </a>

                <xsl:if test="count(descendant::page)&gt;0"><!-- and () -->
                    <ul>
                        <xsl:apply-templates select="page" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>
        
        </xsl:stylesheet>
 
Old March 16th, 2009, 03:05 PM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

I am not sure I understand what you want to achieve. It is not clear whether you want to display all 'page' element ancestors or only the direct parent 'page' of the element you are looking for.
The following stylesheet displays all ancestor 'page' elements and all siblings:
Code:
        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aspdnsf="urn:aspdnsf" exclude-result-prefixes="aspdnsf">
        <xsl:output method="html" indent="yes" omit-xml-declaration="yes" />

        <!-- note - path below should be relative -->        
        <!-- <xsl:variable name="Menu" select="document('/skins/Skin_1/subpagemenu.xml', /)/menu" /> -->
        <xsl:variable name="RequestedPage" select="/root/System/RequestedPage" />

        <xsl:template match="/">
            <ul id="LeftNav">
              <xsl:apply-templates select="descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]" />
            </ul>
        </xsl:template>
        
        <xsl:template match="page">
            <li>
                <!-- current page highlight test: current page or ancestor of current page -->
                <xsl:if test="descendant-or-self::page[@navigateurl = $RequestedPage]"> 
                    <xsl:attribute name="class">active</xsl:attribute>
                </xsl:if>

                <a href="/{@navigateurl}">
                    <xsl:value-of select="@anchortext"/>
                </a>

                <xsl:if test="page[descendant-or-self::page[@navigateurl = $RequestedPage]]"><!-- and () -->
                    <ul>
                        <xsl:apply-templates select="page[descendant-or-self::page[@navigateurl = $RequestedPage] or preceding-sibling::page[@navigateurl = $RequestedPage] or following-sibling::page[@navigateurl = $RequestedPage]]" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>
        
        </xsl:stylesheet>
When run against this sample XML
Code:
<?xml version="1.0" encoding="utf-8"?>
<root>
<System>
  <RequestedPage>t-dr-babizhayev-warning.aspx</RequestedPage>
</System>
<MenuData>

<menu>
  <!-- NOTE - Do not prefix urls with / -->
  <page navigateurl="t-about.aspx" anchortext="About Us">
      <page navigateurl="t-about-returns-policy.aspx" anchortext="Return Policy" />
    <page navigateurl="t-about-privacy-policy.aspx" anchortext="Privacy Policy" />
    <page navigateurl="t-about-terms-and-conditions.aspx" anchortext="Terms &amp; Conditions" />
    <page navigateurl="t-about-accessibility.aspx" anchortext="Accessibility" />
    <page navigateurl="t-about-payment-and-shipping.aspx" anchortext="Payment &amp; Shipping Options" />
    <page navigateurl="t-about-doctor-information.aspx" anchortext="Doctor Information" />
  </page>
  <page navigateurl="c-1-eye-support-products.aspx" anchortext="Eye Support Products">
      <page navigateurl="p-4-n-acetyl-carnosine.aspx" anchortext="N-Acetyl-Carnosine Eye Drops">
        <page navigateurl="t-can-c-testimonials.aspx" anchortext="Testimonials" />
        <page navigateurl="t-can-c-faqs.aspx" anchortext="Can-C FAQs" />
        <page navigateurl="t-can-c-about.aspx" anchortext="About Can-C">
            <page navigateurl="t-can-c-about-ingredients.aspx" anchortext="Can-C Ingredients &amp; Instructions" />
            <page navigateurl="t-can-c-about-how.aspx" anchortext="How and Why Can-C Reverses Cataracts" />
        </page>
        <page navigateurl="t-dr-babizhayev-interviews-and-more.aspx" anchortext="Dr Babizhayev Interviews &amp; More">
            <page navigateurl="t-dr-babizhayev-interview-2001.aspx" anchortext="Dr. Babizhayev interview 2001" />
            <page navigateurl="t-dr-babizhayev-interview-2003.aspx" anchortext="Dr. Babizhayev Interview 2003" />
            <page navigateurl="t-dr-babizhayev-warning.aspx" anchortext="Dr. Babizhayev Warning" />
            <page navigateurl="t-dr-babizhayev-biography.aspx" anchortext="Dr. Babizhayev Biography" />
            <page navigateurl="t-innovative-vision-products.aspx" anchortext="Innovative Vision Products - Company Overview" />
        </page>    
        <page navigateurl="t-can-c-science-and-research.aspx" anchortext="Science &amp; Research">
            <page navigateurl="t-clinical-publications.aspx" anchortext="Clinical Publications" />
            <page navigateurl="#" anchortext="Ocular Support" />
            <page navigateurl="#" anchortext="How and Why Can-C Reverses Cataracts" />
            <page navigateurl="t-can-c-dr-robert-mason-interview.aspx" anchortext="Dr. Robert Mason Interview" />
            <page navigateurl="t-can-c-cataract-surgery-risks.aspx" anchortext="Cataract Surgery Risks - Article" />
        </page>
      </page>
      <page navigateurl="p-6-can-c-plus.aspx" anchortext="Can-C Plus">
        <page navigateurl="#" anchortext="Testimonials" />
        <page navigateurl="#" anchortext="Science &amp; Research" />
      </page>
      <page navigateurl="p-5-can-c-for-dogs.aspx" anchortext="Can-C For Dogs">
        <page navigateurl="t-can-c-dogs-testimonials.aspx" anchortext="Testimonials" />
        <page navigateurl="#" anchortext="Science &amp; Research" />
      </page>
      <page navigateurl="p-7-macular-support-armd.aspx" anchortext="Macular Support (ARMD)">
        <page navigateurl="#" anchortext="Testimonials" />
        <page navigateurl="#" anchortext="Science &amp; Research" />
      </page>
  </page>
  <page navigateurl="t-eyeconditioninfo.aspx" anchortext="Eye Condition Info">
    <page navigateurl="#" anchortext="Degenerative Eye Disease" />
    <page navigateurl="#" anchortext="Cataracts" />
    <page navigateurl="#" anchortext="Dry Eye Condition" />
    <page navigateurl="#" anchortext="Macular Degeneration" />
    <page navigateurl="#" anchortext="Glaucoma" />
    <page navigateurl="#" anchortext="Presbyopia" />
    <page navigateurl="#" anchortext="Retinal Disease" />
    <page navigateurl="#" anchortext="Floaters (Vitreous Opacities)" />
    <page navigateurl="#" anchortext="Corneal Disorders" />
    <page navigateurl="#" anchortext="Ocular Inflamation" />
  </page>
</menu>
</MenuData>
</root>
the output is
Code:
<ul id="LeftNav">
   <li class="active"><a href="/c-1-eye-support-products.aspx">Eye Support Products</a><ul>
         <li class="active"><a href="/p-4-n-acetyl-carnosine.aspx">N-Acetyl-Carnosine Eye Drops</a><ul>
               <li class="active"><a href="/t-dr-babizhayev-interviews-and-more.aspx">Dr Babizhayev Interviews &amp; More</a><ul>
                     <li><a href="/t-dr-babizhayev-interview-2001.aspx">Dr. Babizhayev interview 2001</a></li>
                     <li><a href="/t-dr-babizhayev-interview-2003.aspx">Dr. Babizhayev Interview 2003</a></li>
                     <li class="active"><a href="/t-dr-babizhayev-warning.aspx">Dr. Babizhayev Warning</a></li>
                     <li><a href="/t-dr-babizhayev-biography.aspx">Dr. Babizhayev Biography</a></li>
                     <li><a href="/t-innovative-vision-products.aspx">Innovative Vision Products - Company Overview</a></li>
                  </ul>
               </li>
            </ul>
         </li>
      </ul>
   </li>
</ul>
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
 
Old March 16th, 2009, 03:08 PM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

It looks to me like

<xsl:apply-templates select="/root/MenuData/menu/page[@navigateurl=$RequestedPage]" />
but you seem to have made the problem much more complicated than it is, perhaps I have misunderstood?
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old March 17th, 2009, 05:57 AM
Registered User
 
Join Date: Mar 2009
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Martin you are amazing, thank you so much! This has saved my project from going over its deadline, I cant tell you how much I appreciate this!

I made one small change to the code you posted to remove the attribute checks in the template recursion. This let it show all its neighbours instead of just the current page.

So I changed:
Code:
                <xsl:if test="page[descendant-or-self::page[@navigateurl = $RequestedPage]]"><!-- and () -->
                    <ul>
                        <xsl:apply-templates select="page[descendant-or-self::page[@navigateurl = $RequestedPage] or preceding-sibling::page[@navigateurl = $RequestedPage] or following-sibling::page[@navigateurl = $RequestedPage]]" />
                    </ul>
                </xsl:if>
To:
Code:
                <xsl:if test="page[descendant-or-self::page]"><!-- and () -->
                    <ul>
                        <xsl:apply-templates select="page[descendant-or-self::page or preceding-sibling::page[@navigateurl = $RequestedPage] or following-sibling::page[@navigateurl = $RequestedPage]]" />
                    </ul>
                </xsl:if>
Final code:
Code:
        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aspdnsf="urn:aspdnsf" exclude-result-prefixes="aspdnsf">
        <xsl:output method="html" omit-xml-declaration="yes" />

        <!-- note - path below should be relative -->        
        <!-- <xsl:variable name="Menu" select="document('/skins/Skin_1/subpagemenu.xml', /)/menu" /> -->
        <xsl:variable name="RequestedPage" select="/root/System/RequestedPage" />
        
        <xsl:template match="/">
           <xsl:if test="count(descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]) &gt; 0">
            <ul id="LeftNav">
              <xsl:apply-templates select="descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]" />
            </ul>
           </xsl:if>
        </xsl:template>

        <xsl:template match="page">
            <li>
                <!-- current page highlight test: current page or ancestor of current page -->
                <xsl:if test="descendant-or-self::page[@navigateurl = $RequestedPage]"> 
                    <xsl:attribute name="class">active</xsl:attribute>
                </xsl:if>

                <a href="/{@navigateurl}">
                    <xsl:value-of select="@anchortext"/>
                </a>

                <xsl:if test="page[descendant-or-self::page]"><!-- and () -->
                    <ul>
                        <xsl:apply-templates select="page[descendant-or-self::page or preceding-sibling::page[@navigateurl = $RequestedPage] or following-sibling::page[@navigateurl = $RequestedPage]]" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>

        
        </xsl:stylesheet>
Again, thank you so much!

I usually help out over on the asp.net forums and there is a button to mark as answer which gives the poster extra points. Cant see it anywhere here, is there a similar scheme?
 
Old March 17th, 2009, 06:00 AM
Registered User
 
Join Date: Mar 2009
Posts: 3
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Oh yeah I forgot I also added a count() check to the initial template so that it didnt output an empty <ul> on pages that didnt need a menu.
 
Old March 17th, 2009, 09:38 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

Note that with XSLT if you want to check that an expression selects at least one node then you can simply check e.g.
Code:
<xsl:if test="foo">
instead of
Code:
<xsl:if test="count(foo) &gt; 0">
The first is more XSLT like and should be more efficient too.
So you can change
Code:
        <xsl:template match="/">
           <xsl:if test="count(descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]) &gt; 0">
            <ul id="LeftNav">
              <xsl:apply-templates select="descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]" />
            </ul>
           </xsl:if>
        </xsl:template>
to
Code:
        <xsl:template match="/">
           <xsl:if test="descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]">
            <ul id="LeftNav">
              <xsl:apply-templates select="descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]" />
            </ul>
           </xsl:if>
        </xsl:template>
And after your changes the other template can be simplified even more as you currently have checks in the form
Code:
<xsl:if test="page[descendant-or-self::page]">
which can be simplified to
Code:
<xsl:if test="page">
as in 'page[descendant-or-self::page]' the 'self::page' is always true.

So you could simplify your stylesheet
Code:
        <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:aspdnsf="urn:aspdnsf" exclude-result-prefixes="aspdnsf">
        <xsl:output method="html" omit-xml-declaration="yes" />

        <!-- note - path below should be relative -->        
        <!-- <xsl:variable name="Menu" select="document('/skins/Skin_1/subpagemenu.xml', /)/menu" /> -->
        <xsl:variable name="RequestedPage" select="/root/System/RequestedPage" />
        
        <xsl:template match="/">
           <xsl:if test="descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]">
            <ul id="LeftNav">
              <xsl:apply-templates select="descendant::root/MenuData/menu/page[descendant-or-self::page[@navigateurl = $RequestedPage]]" />
            </ul>
           </xsl:if>
        </xsl:template>

        <xsl:template match="page">
            <li>
                <!-- current page highlight test: current page or ancestor of current page -->
                <xsl:if test="descendant-or-self::page[@navigateurl = $RequestedPage]"> 
                    <xsl:attribute name="class">active</xsl:attribute>
                </xsl:if>

                <a href="/{@navigateurl}">
                    <xsl:value-of select="@anchortext"/>
                </a>

                <xsl:if test="page"><!-- and () -->
                    <ul>
                        <xsl:apply-templates select="page" />
                    </ul>
                </xsl:if>
            </li>
        </xsl:template>

        
        </xsl:stylesheet>
to make it execute faster and becomes easier understandable and maintainable.
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog





Similar Threads
Thread Thread Starter Forum Replies Last Post
append a xml node from one file to another pravind XSLT 5 August 22nd, 2009 09:15 AM
Update the node value of XML file Manoj Bisht ASP.NET 3.5 Professionals 6 February 19th, 2009 09:36 AM
Select XML Node - Name space qualified aintvoguish XSLT 2 December 28th, 2007 03:45 PM
XML: How to select a node with ' in it using Xpath guozai BOOK: Professional C#, 2nd and 3rd Editions 1 October 6th, 2006 12:37 PM
Adding an Element with Attribute to XML file xergic Classic ASP XML 0 November 20th, 2004 08:26 AM





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