Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
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
Medium Priority
?
359 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 7
  • 4
  • 2
14 Comments
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 600 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 400 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
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

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

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Question has a verified solution.

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

Most of the sites are being standardized with W3C Web Standards. W3C provides lot of web standard services to the web. They have the web specification, process and documentation for all the web standards. You can apply HTML, CSS and Accessibility st…
JavaScript has plenty of pieces of code people often just copy/paste from somewhere but never quite fully understand. Self-Executing functions are just one good example that I'll try to demystify here.
Viewers will learn one way to get user input in Java. Introduce the Scanner object: Declare the variable that stores the user input: An example prompting the user for input: Methods you need to invoke in order to properly get  user input:
The viewer will learn the basics of jQuery, including how to invoke it on a web page. Reference your jQuery libraries: (CODE) Include your new external js/jQuery file: (CODE) Write your first lines of code to setup your site for jQuery.: (CODE)

604 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