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
351 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
Simplifying Server Workload Migrations

This use case outlines the migration challenges that organizations face and how the Acronis AnyData Engine supports physical-to-physical (P2P), physical-to-virtual (P2V), virtual to physical (V2P), and cross-virtual (V2V) migration scenarios to address these challenges.

 

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

Optimizing Cloud Backup for Low Bandwidth

With cloud storage prices going down a growing number of SMBs start to use it for backup storage. Unfortunately, business data volume rarely fits the average Internet speed. This article provides an overview of main Internet speed challenges and reveals backup best practices.

Question has a verified solution.

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

Suggested Solutions

When you work with shopping cart / ecommerce relates web sites, you need to pass the certain form post details to the payment gateway process page with required details for the products items you give to order. Also you may need to track the ordered…
Preface This article introduces an authentication and authorization system for a website.  It is understood by the author and the project contributors that there is no such thing as a "one size fits all" system.  That being said, there is a certa…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn the benefit of using external CSS files and the relationship between class and ID selectors. Create your external css file by saving it as style.css then set up your style tags: (CODE) Reference the nav tag and set your prop…

831 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