Solved

How would I display a simple recursive relationship in XSL starting from the node that doesn't have a idref?

Posted on 2009-04-08
14
352 Views
Last Modified: 2013-11-18
          I have an XSD file as a schema and a sample XML file. I am trying to get the XSL to display information about rank or position by following the relationship chain by using the id and the idref in the files. Basically I'm trying to display the information so that the node without a idref will be listed first, and then the node that references the node without a ref will be listed second, and then the node that references the second will be listed third. For example,

<cat>
<name>mellow</name>
<id>c1</id>
</cat>


<cat>
<name>apple</name>
<id>c3<id/>
<idref>c2</idref>
</cat>

<cat>
<name>miles</name>
<id>c2<id/>
<idref>c1</idref>
</cat>


basically I want to display cat c1 first, c2 second, and c3 third by following the id, idref chain.
0
Comment
Question by:AlexanderMaxwell
  • 7
  • 4
  • 2
14 Comments
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 150 total points
ID: 24098322
You can use a key to index all the cat elements, based on there idref

I added a cats root element to get started
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:key name="ref" match="cat" use="idref"/>
    <xsl:template match="cats">
        <xsl:apply-templates select="cat[not(idref)]"></xsl:apply-templates>
    </xsl:template>
    <xsl:template match="cat">
        <xsl:value-of select="name"/>
        <xsl:apply-templates select="key('ref', id)"></xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24098368
The code starts with the root element and selects in an apply-templates the cat that does not have an idref
in the cat template I call out for the indexed cat element that has the correct idref, using the key function
0
 
LVL 20

Assisted Solution

by:ChristoferDutz
ChristoferDutz earned 100 total points
ID: 24098407
Hi

I hope I understood your question right, but I think this Xsl should do the trick:

It iterates over all cat elements with no idref-element and then delegates to the outputCat template to output all elements referencing the current id. When outputing any nodes referencing, the template recursively calls itself to output any cat referencing the currently output cat.

Chris
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:template match="/">
        <xsl:for-each select="//cat[count(idref) = 0]">
            <xsl:value-of select="name"/>
            <xsl:call-template name="outputCat">
                <xsl:with-param name="context" select="."/>
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>
    <xsl:template name="outputCat">
        <xsl:param name="context" select="."/>
        <xsl:for-each select="//cat[idref = $context/id]">
            <xsl:value-of select="name"/>
            <xsl:call-template name="outputCat">
                <xsl:with-param name="context" select="."/>
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
Free Tool: Site Down Detector

Helpful to verify reports of your own downtime, or to double check a downed website you are trying to access.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 

Author Comment

by:AlexanderMaxwell
ID: 24098704
I realizes I didn't attach my code snippet, thanks for the help so far guys, I'll take a look at them. I'll let you guys see my code, so you can understand what exactly I'm trying to learn.
XSD:
 
 
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <!--university catalog-->
    <xs:element name="UniversitiesOfNation">
        <xs:complexType>
            <xs:sequence>
               
                <xs:element name = "Region" type = "Regiontype" minOccurs="1" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
<!--region-->
    <xs:complexType name="Regiontype" >
        <xs:sequence>
            <xs:element name="Country" type="xs:string"/>
            <xs:element name="ISO" type="xs:string"/>
            <xs:element name="Population" type="xs:int"/>
            <xs:element name="PremierUniversity" type="GenAreatype" minOccurs="1" maxOccurs="1"/>
            <xs:element name="GenArea" type="GenAreatype" minOccurs="1" maxOccurs="unbounded"/> 
        </xs:sequence>
    </xs:complexType> 
    
    
    
    <!--state-->
    <xs:complexType name="GenAreatype" >
        <xs:sequence>
        <xs:element name="State" type="xs:string"/>
        <xs:element name="Area" type="Areatype" minOccurs="1" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType> 
    
    <!--city-->
    <xs:complexType name="Areatype" >
        <xs:sequence>
            <xs:element name="city" type="xs:string"/>
            <xs:element name="university" type="universitytype" minOccurs="1" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType> 
    
<!--university-->
    
    <!--university-->
    <xs:complexType name="universitytype" >
        <xs:sequence>
            <xs:element name="Name" type="xs:string"/>
            <xs:element name="UniversityID" type="xs:ID" minOccurs="1" maxOccurs="1"/>
            <xs:element name="PrecedingUniversityID" type="xs:IDREF" minOccurs="0" maxOccurs="1"/>
            <xs:element name="Address" type="addresstype" minOccurs="0" maxOccurs="1"/>
            <xs:element name="Department" type="departmenttype" minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>  
 
 
    <!--address-->
    
    <xs:complexType name="addresstype" >
        <xs:sequence> 
            <xs:element name="street" type="xs:string"/>
            <xs:element name="zipcode" type="xs:integer"/>
        </xs:sequence>
    </xs:complexType>
    
    <!--departmenttype-->
    <xs:complexType name="departmenttype" >
        <xs:sequence> 
            <xs:element name="DepartmentName" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
 
</xs:schema>
 
 
 
 
XML:
 
 
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="rank.xsl"?>
<UniversitiesOfNation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="UniversitiesOfCountry.xsd">
    <Region>
        <Country>United States of America</Country>
        <ISO>US</ISO>
        <Population>300000000</Population>
        <PremierUniversity>
             <State>massachusetts</State>
            <Area>
                <city>Cambridge</city>
                <university>         
                    <Name>Harvard</Name>
                    <UniversityID>U1</UniversityID> 
                    
                    <Address>
                        <street>201 Cambridge st</street>
                        <zipcode>12345</zipcode>
                    </Address>
                    <Department>
                        <DepartmentName>INFO</DepartmentName>
                    </Department>
                    
                </university>
            </Area>
        </PremierUniversity>
        <GenArea>
            <State>GA</State>
            <Area>
                <city>Macon</city>
                <university>
                    
                    <Name>Macon State University</Name>
                    <UniversityID>U2</UniversityID>
                    <PrecedingUniversityID>U1</PrecedingUniversityID>
                    <Address>
                        <street>2234 Hope Street</street>
                        <zipcode>31078</zipcode>
                    </Address>
                    <Department>
                        <DepartmentName>Maps</DepartmentName>
                    </Department>
                    <Department>
                        <DepartmentName>MIS</DepartmentName>
                    </Department>
                    
                    
                </university>
                <university>
                   
                    <Name>Wesleyn</Name>
                    
                    
                    <UniversityID>U4</UniversityID>
                    <PrecedingUniversityID>U3</PrecedingUniversityID>
                    <Address>
                        <street>55 Street Street</street>
                        <zipcode>31078</zipcode>
                    </Address>
                    <Department>
                        <DepartmentName>MIS</DepartmentName>
                       
                    </Department>
                    
                </university> 
                <university>
                 
                    <Name>Mercer</Name>
                    <UniversityID>U3</UniversityID>
                    <PrecedingUniversityID>U2</PrecedingUniversityID>
                    <Address>
                        <street>200 Baldwin Street</street>
                        <zipcode>31078</zipcode>
                    </Address>
                </university> 
            </Area>
            <Area>
                <city>Valdosta</city>
                <university>
               
                    <Name>Valdosta University</Name>
                    <UniversityID>U5</UniversityID>
                    <PrecedingUniversityID>U4</PrecedingUniversityID>
                    <Address>
                        <street>333 hame Street</street>
                        <zipcode>11234</zipcode>
                    </Address>
                </university>
                <university>
                    <Name>HAVOC University</Name>
                    <UniversityID>U6</UniversityID>
                    <PrecedingUniversityID>U5</PrecedingUniversityID>
                    <Address>
                        <street>123 Ny Street</street>
                        <zipcode>44534</zipcode>
                    </Address>
                </university>
            </Area>
        </GenArea>
    </Region>
    <Region>
        <Country>China</Country>
        <ISO>CN</ISO>
        <Population>1500000000</Population>
        <PremierUniversity>
            <State>none</State>
            <Area>
                <city>Hong Kong</city>
                <university>
                    <Name>University of China</Name>
                    <UniversityID>U7</UniversityID>
                    <PrecedingUniversityID>U6</PrecedingUniversityID>
                    <Address>
                        <street>Beijing st</street>
                        <zipcode>54321</zipcode>
                    </Address>
                    <Department>
                        <DepartmentName>MIS</DepartmentName>
                    </Department>
                </university>
            </Area>
        </PremierUniversity>
        <GenArea>
            <State>none</State>
            <Area>
                <city>Beijing</city>
                <university>
                    <Name>Beijing Univeristy</Name>
                    <UniversityID>U8</UniversityID>
                    <PrecedingUniversityID>U7</PrecedingUniversityID>
                    <Address>
                        <street>21058 Japan st</street>
                        <zipcode>22331</zipcode>
                    </Address>
                </university>
            </Area>
        </GenArea>
    </Region>
    
</UniversitiesOfNation>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24098901
@ChristopherDutz,

If you allow me, I have some suggestions for your stylesheet,
I hope you appreciate the suggestions

1. <xsl:for-each select="//cat[count(idref) = 0]">
It is considered good XSLT design to avoid for-each in this kind of situations.
for-each will cause a certain nesting in your programming code, which will make your code highly unmaintainable in the end
this nesting can be avoide by using apply-templates and seperate templates

2. <xsl:for-each select="//cat[idref = $context/id]">
This is a real performance killer
with // you are traversing the entire document tree looking for elements
This is a very small XML set, but that still is no reason to not apply good practice
Not using "//cat" but using "/cats/cat" would improve performance a lot
Using keys, as I did in my proposed solution is the real way to go

3. There is no need for making a named template.
Building recursion using named templates usually is a good thing, if you are not simply jumping element nodes as in this example.
XSLT processors are optimised for dealing with apply-template processing
Not every XSLT processor has the same optimisation for recursive named template processing... so there might be too much information stored on the stack

4. a smaller issue
<xsl:for-each select="//cat[count(idref) = 0]">
would be better done by not counting but using the default boolean
<xsl:for-each select="//cat[not(idref)]">

Hope you like this and learn from this.
I am sure you would benefit from reviewing my proposed solution

cheers

Geert
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24099026
@AlexanderMaxwell,
I am sorry but I don't understand why you accept ChristopherDutz' answer.
1. It was not the first correct answer given.
2. Mine is superior over the other
Policy of EE is that you accept the first correct answer or split points
I don't care for the points but
1. Now others that find this answer in the database will learn the wrong things, and things are severely wrong as you can see from my follow up
2. I am working for this years "sniper" (answers hit performance), so this question is really bad for my statistics.
I would appreciate that you reopen the question and accept the right answer,
thanks for that
0
 
LVL 20

Expert Comment

by:ChristoferDutz
ID: 24099095
I have to admit, that I liked the other solution much better too :-) So go ahead :-)
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24099130
My code, applied to your XML,
creating an HTML table of universities
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:key name="ref" match="university" use="PrecedingUniversityID"/>
    <xsl:template match="/">
        <table border="1">
            <xsl:apply-templates select="//university[not(PrecedingUniversityID)]"></xsl:apply-templates>
            
        </table>
    </xsl:template>
    <xsl:template match="university">
        <tr>
            <td><xsl:value-of select="UniversityID"/></td>
            <td><xsl:value-of select="PrecedingUniversityID"/></td>
            <td><xsl:value-of select="Name"/></td>
            <td><xsl:value-of select="ancestor::Area/city"/></td>
        </tr>
        <xsl:value-of select="name"/>
        <xsl:apply-templates select="key('ref', UniversityID)"></xsl:apply-templates>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
 

Author Comment

by:AlexanderMaxwell
ID: 24099164
I'm sorry Gertrone, I'm still examining the answers given. However, for my problem, the given solution seemed to work. I'm new here so I guess I didn't wait long enough before evaluating. In the future I will be more careful.

Thanks,

Alexander
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24099409
well, pay attention to my comments on the accepted solution, I hope you learn from them... a lot of good tips in there
0
 

Author Comment

by:AlexanderMaxwell
ID: 24100073
No offenses to ChristoferDutz, but I would reopen the question, but I don't know how. I'm still pretty new.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24100176
There is a link in the original question
"Request Attention"
Click it and describe what you want to do.
It will then be posted to community support

I suggest that you ask for a split points (after all, Christopher gave a working solution too)
I don't care how much points I get exactly in the split,
after a split, both solutions wil be highlighted if someone hits this question searching the EE database,
I am happy as long as attention is drawn to the correct solution too

0
 

Author Closing Comment

by:AlexanderMaxwell
ID: 31568072
Thanks, this worked fine. I'm a beginner so I can't quite tell what's going on, but I think I can follow it.
0

Featured Post

Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

Title # Comments Views Activity
Forbidden errors 5 152
Technology stack suggestions 4 67
Problem to page 4 93
Where does legacy ASP.NET initialize language? 2 26
I will show you how to create a ASP.NET Captcha control without using any HTTP HANDELRS or what so ever. you can easily plug it into your web pages. For Example a = 2 + 3 (where 2 and 3 are 2 random numbers) Session("Answer") = 5 then we…
I found this questions asking how to do this in many different forums, so I will describe here how to implement a solution using PHP and AJAX. The logical flow for the problem should be: Write an event handler for the first drop down box to get …
Viewers will learn about basic arrays, how to declare them, and how to use them. Introduction and definition: Declare an array and cover the syntax of declaring them: Initialize every index in the created array: Example/Features of a basic arr…
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.

828 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question