Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations gkittelson on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

XSL - get previous nodes value?

Status
Not open for further replies.

jdbolt

Programmer
Aug 10, 2005
89
CA
Here is an example of the XML document I am working with:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<root type='foo'>
<child>
<modifier>BBCI</modifier>
<url>[URL unfurl="true"]http://www.foo.com</url>[/URL]
<category>foo</category>
<categorydescription>The foobar category</categorydescription>
<linkdescription>This is the BBCI Link</linkdescription>
<displaytext>BBCI Link</displaytext>
<description>This is the BBCI Link</description>

<cascadelinks>1</cascadelinks>
</child>
<child>
<modifier>DMPORTLS</modifier>
<url>foo.com</url>
<category>foo</category>
<categorydescription>The foobar category</categorydescription>
<linkdescription>description</linkdescription>
<displaytext>fooDisplay</displaytext>
<description>description</description>

<cascadelinks>1</cascadelinks>
</child>
<child>
<modifier>DMPORTLS</modifier>
<url>[URL unfurl="true"]http://www.slashdot.org</url>[/URL]
<category>Test1</category>
<categorydescription>This category will be useful</categorydescription>
<linkdescription>All things tech...</linkdescription>
<displaytext>News for nerds, stuff that matters</displaytext>
<description>All things tech...</description>

<cascadelinks>1</cascadelinks>
</child>
</root>

I need a way to group this document by class (I cant change the xml doc itself). SO I have teh following xsl doc:

Code:
<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]

	<xsl:template match="/">

		
		<xsl:for-each select="/root/child">
			<xsl:sort select="category" order="ascending"/>
		
			<xsl:if test="/root/child[position()]/category != /root/child[position()-1]/category or position() = 1">
				<h2>
					<xsl:value-of select="category" />
				</h2>
				<xsl:value-of select="categorydescription" />
			</xsl:if>
			<ul>
				<li>
					<a>
						<xsl:attribute name="href">
							<xsl:value-of select="url" />
						</xsl:attribute>
						<xsl:attribute name="title">
							<xsl:value-of select="description" />
						</xsl:attribute>
						<xsl:value-of select="displaytext"/>
					</a>
				</li>
			</ul>
		</xsl:for-each>
	
	</xsl:template>

</xsl:stylesheet>

As you can see, I want to display a category name and descritpion only when its the first instance of that category.

With this code I get the following:

Test1
This category will be useful

* News for nerds, stuff that matters

* BBCI Link

* fooDisplay

It doesnt display the 'foo category for items 2 and 3. I'm not sure why. Could anyone help me with this? Am i using position() correctly?
 
As soon as you change the context you lose the position of the previous context, so you can't do it like that. To use the previous position you need to store it in a variable, however this wouldn't help in this case either, because the way sort works (the position() refers to the original list, not the sorted list). There are two ways to group in XSL, using keys or this way, which says get all childs that have a category that does not appear in a preceding sibling:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
  <xsl:template match="/">
    <xsl:for-each select="/root/child[not(category = preceding-sibling::child/category)]">
      <h2>
        <xsl:value-of select="category"/>
      </h2>
      <xsl:value-of select="categorydescription"/>
      <ul>
        <xsl:for-each select="/root/child[category = current()/category]">
          <li>
            <a>
              <xsl:attribute name="href"><xsl:value-of select="url"/></xsl:attribute>
              <xsl:attribute name="title"><xsl:value-of select="description"/></xsl:attribute>
              <xsl:value-of select="displaytext"/>
            </a>
          </li>
        </xsl:for-each>
      </ul>
    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
Personally, I'd prefer to tidy this up by using templates more semantically:
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]
  <xsl:template match="/">
    <xsl:apply-templates select="/root/child[not(category = preceding-sibling::child/category)]" mode="group"/>
  </xsl:template>
  <xsl:template match="child" mode="group">
    <h2>
      <xsl:value-of select="category"/>
    </h2>
    <xsl:value-of select="categorydescription"/>
    <ul>
      <xsl:apply-templates select="/root/child[category = current()/category]" mode="list"/>
    </ul>
  </xsl:template>
  <xsl:template match="child" mode="list">
    <li>
      <a>
        <xsl:attribute name="href"><xsl:value-of select="url"/></xsl:attribute>
        <xsl:attribute name="title"><xsl:value-of select="description"/></xsl:attribute>
        <xsl:value-of select="displaytext"/>
      </a>
    </li>
  </xsl:template>
</xsl:stylesheet>

Jon

"I don't regret this, but I both rue and lament it.
 
Wow, thats amazing thanks alot, the second it easier for me to understand, so I think I will stick with that.

Thanks!
 
I think the op's original approach is possible with sort recognized as well. Just need to tighten up some loose end.
[tt]
<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0" xmlns:xsl="
<xsl:template match="/">

<xsl:for-each select="/root/child">
<xsl:sort select="category" order="[red]descending[/red]"/>

<xsl:if test="[red]not(self::category = following-sibling::category)[/red] or position() = 1">
<h2>
<xsl:value-of select="category" />
</h2>
<xsl:value-of select="categorydescription" />
</xsl:if>
<ul>
<li>
<a>
<xsl:attribute name="href">
<xsl:value-of select="url" />
</xsl:attribute>
<xsl:attribute name="title">
<xsl:value-of select="description" />
</xsl:attribute>
<xsl:value-of select="displaytext"/>
</a>
</li>
</ul>
</xsl:for-each>

</xsl:template>

</xsl:stylesheet>
[/tt]
 
Amendment

I missed out a reference to child in the conditional. I relist it!
Code:
<?xml version="1.0" encoding="ISO-8859-1"?>

<xsl:stylesheet version="1.0" xmlns:xsl="[URL unfurl="true"]http://www.w3.org/1999/XSL/Transform">[/URL]

    <xsl:template match="/">
        
        <xsl:for-each select="/root/child">
            <xsl:sort select="category" order="descending"/>
        
            <xsl:if test="not(self::child/category = following-sibling::child/category) or position() = 1">
                <h2>
                    <xsl:value-of select="category" />
                </h2>
                <xsl:value-of select="categorydescription" />
            </xsl:if>
            <ul>
                <li>
                    <a>
                        <xsl:attribute name="href">
                            <xsl:value-of select="url" />
                        </xsl:attribute>
                        <xsl:attribute name="title">
                            <xsl:value-of select="description" />
                        </xsl:attribute>
                        <xsl:value-of select="displaytext"/>
                    </a>
                </li>
            </ul>
        </xsl:for-each>
    
    </xsl:template>

</xsl:stylesheet>
 
That will display category for last item in each group. Need to use:
Code:
<xsl:if test="not(self::child/category = preceding-sibling::child/category)">

Jon

"I don't regret this, but I both rue and lament it.
 
After I get a chance to do a rough test, it seems that it is my original
[tt] not(self::child/category=following-sibling::child/category)[/tt]
which gives the good renderment, after all! It has something to do with the behaviour of the sort descending setting of the category.
 
It doesn't do what is intended. It outputs the title above last item in the category instead of the first.

With the XML in the first post, the "BBCI Link" should be under 'foo' but with your stylesheet it goes under "Test1". For better illustration use this XML input:
Code:
<root type="foo">
  <child>
    <modifier>DMPORTLS</modifier>
    <url>[URL unfurl="true"]http://www.slashdot.org</url>[/URL]
    <category>Test1</category>
    <categorydescription>This category will be useful</categorydescription>
    <linkdescription>All things tech...</linkdescription>
    <displaytext>News for nerds, stuff that matters</displaytext>
    <description>All things tech...</description>
    <cascadelinks>1</cascadelinks>
  </child>
  <child>
    <modifier>BBCI</modifier>
    <url>[URL unfurl="true"]http://www.foo.com</url>[/URL]
    <category>foo</category>
    <categorydescription>The foobar category</categorydescription>
    <linkdescription>This is the BBCI Link</linkdescription>
    <displaytext>BBCI Link</displaytext>
    <description>This is the BBCI Link</description>
    <cascadelinks>1</cascadelinks>
  </child>
    <child>
    <modifier>BBCI</modifier>
    <url>[URL unfurl="true"]http://www.foo.com</url>[/URL]
    <category>foo</category>
    <categorydescription>The foobar category</categorydescription>
    <linkdescription>This is the BBCI Link</linkdescription>
    <displaytext>BBCI Link</displaytext>
    <description>This is the BBCI Link</description>
    <cascadelinks>1</cascadelinks>
  </child>
  <child>
    <modifier>DMPORTLS</modifier>
    <url>foo.com</url>
    <category>foo</category>
    <categorydescription>The foobar category</categorydescription>
    <linkdescription>description</linkdescription>
    <displaytext>fooDisplay</displaytext>
    <description>description</description>
    <cascadelinks>1</cascadelinks>
  </child>
  <child>
    <modifier>DMPORTLS</modifier>
    <url>[URL unfurl="true"]http://www.slashdot.org</url>[/URL]
    <category>Test1</category>
    <categorydescription>This category will be useful</categorydescription>
    <linkdescription>All things tech...</linkdescription>
    <displaytext>News for nerds, stuff that matters</displaytext>
    <description>All things tech...</description>
    <cascadelinks>1</cascadelinks>
  </child>
</root>

Jon

"I don't regret this, but I both rue and lament it.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top