Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 363
  • Last Modified:

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

          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
AlexanderMaxwell
Asked:
AlexanderMaxwell
  • 7
  • 4
  • 2
2 Solutions
 
Geert BormansCommented:
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
 
Geert BormansCommented:
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
 
ChristoferDutzCommented:
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
Independent Software Vendors: 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!

 
AlexanderMaxwellAuthor Commented:
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
 
Geert BormansCommented:
@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
 
Geert BormansCommented:
@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
 
ChristoferDutzCommented:
I have to admit, that I liked the other solution much better too :-) So go ahead :-)
0
 
Geert BormansCommented:
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
 
AlexanderMaxwellAuthor Commented:
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
 
Geert BormansCommented:
well, pay attention to my comments on the accepted solution, I hope you learn from them... a lot of good tips in there
0
 
AlexanderMaxwellAuthor Commented:
No offenses to ChristoferDutz, but I would reopen the question, but I don't know how. I'm still pretty new.
0
 
Geert BormansCommented:
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
 
AlexanderMaxwellAuthor Commented:
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

What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

  • 7
  • 4
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now