?
Solved

XSL Tree for-each

Posted on 2009-04-21
19
Medium Priority
?
388 Views
Last Modified: 2013-11-18
I am looking to build a tree from a bill of material using XSL. I able to get each individual level, but do not know how to do a nested loop to pull it all together. My XML file is formatted as follows (example of 3 levels): If I change the ="*/*" to "*/*/*/*" I get the second level, and if I use "*/*/*/*/*/*" I get level 3.

How do I nest these together to get one indented tree with the children underneath the parent.

:
<SUBPARTS>
<PART>
<ID></ID>
<DESCRIPTION></DESCRIPTION>
<PARENT></PARENT>
</PART>
<PART>
<ID></ID>
<DESCRIPTION></DESCRIPTION>
<PARENT></PARENT>
</PART>

<PART>
<ID></ID>
<DESCRIPTION></DESCRIPTION>
<PARENT></PARENT>
<SUBPARTS>
<PART>
<ID></ID>
<DESCRIPTION></DESCRIPTION>
<PARENT></PARENT>
<SUBPARTS>
<PART>
<ID></ID>
<DESCRIPTION></DESCRIPTION>
<PARENT></PARENT>
</PART>
</SUBPART>
</PART>
</SUBPART>
</PART>
</SUBPART>




<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:template match="/">
  <html>
  <body>
  <h2>My Parts</h2>
  <table border="1">
    <tr bgcolor="#9acd32">
      <th>ID</th>
      <th>Description</th>
      <th>Parent</th>
    </tr>
    <xsl:for-each select="*/*">
    <tr>
      <td><xsl:value-of select="ID"/></td>
      <td><xsl:value-of
select="Description"/></td>
<td><xsl:value-of select="Parent"/></td>
    </tr>
    </xsl:for-each>
0
Comment
Question by:phil301
  • 9
  • 7
  • 3
19 Comments
 
LVL 39

Expert Comment

by:abel
ID: 24199520
Why did you post in a DB zone? The XSLT zone and XML would've been excellent for this :)

> How do I nest these together to get one indented tree with the children underneath the parent.

not so sure what you mean by this, but templates in XSLT are the means to work with input data. For-each is sometimes a convenience, but not when you need to loop through several levels. Hold on, I'll give an example.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24199536
In order to achieve this, you should use apply-templates and seperate templates instead of for-each
I have two questions
- could it be that you have a missbalanced XML and that the closing tag for <SUBPARTS> is wrongly </SUBPART>?
- can you please post data with some content in it and the result you want?

cheers

Geert
0
 
LVL 39

Expert Comment

by:abel
ID: 24199546
hmm, your data is not complete. Can you paste a valid piece of XML so that I can be sure that I work on the input you want me to work on?
0
What is SQL Server and how does it work?

The purpose of this paper is to provide you background on SQL Server. It’s your self-study guide for learning fundamentals. It includes both the history of SQL and its technical basics. Concepts and definitions will form the solid foundation of your future DBA expertise.

 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24199621
I twiddled a bit with your data, I think this is what you need.
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    >
    <xsl:template match="/">
        <html>
            <body>
                <h2>My Parts</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th>ID</th>
                        <th>Description</th>
                        <th>Parent</th>
                    </tr>
                    <xsl:apply-templates select="SUBPART/PART"></xsl:apply-templates>
                </table>
            </body>
        </html>
    </xsl:template>
    
    <xsl:template match="PART">
        <tr>
            <td><xsl:value-of select="ID"/></td>
            <td><xsl:value-of 
                select="DESCRIPTION"/></td>
            <td>
                <xsl:choose>
                    <xsl:when test="ancestor::PART">
                        <xsl:value-of select="ancestor::PART[1]/ID"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:text>TOP</xsl:text>
                    </xsl:otherwise>
                </xsl:choose>
            </td>
        </tr>
        <xsl:apply-templates select="SUBPART/PART"></xsl:apply-templates>
    </xsl:template>
    
</xsl:stylesheet>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24199638
This is the data I used,

Note in the XSLT that I push out the selected nodes (SUBPART/PART)
to the relevant template, which is the PART template,
and after creating the row, I continu doing this nested work with the possible SUBPART/PART children

This is how you do hierarchical processing in XSLT
Doing this with for-each, will result in very deeply nested code, and that is hardly maintainable
<?xml version="1.0" encoding="UTF-8"?>
<SUBPART>
    <PART>
        <ID>A</ID>
        <DESCRIPTION>FOO-A</DESCRIPTION>
    </PART>
    <PART>
        <ID>B</ID>
        <DESCRIPTION>FOO-B</DESCRIPTION>
    </PART>
    <PART>
        <ID>C</ID>
        <DESCRIPTION>FOO-C</DESCRIPTION>
        <SUBPART>
            <PART>
                <ID>D</ID>
                <DESCRIPTION>FOO-D</DESCRIPTION>
                <SUBPART>
                    <PART>
                        <ID>E</ID>
                        <DESCRIPTION>FOO-E</DESCRIPTION>
                    </PART>
                </SUBPART>
            </PART>
        </SUBPART>
    </PART>
</SUBPART>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24199684
Hi abel, I did not see your first post, only your second
seems like your example would have gone in the same direction anyway

@phil, with "I push out the selected nodes", I mean that is what I do with the apply-templates.
You actually select a bunch of nodes and you evaluate them against the match attributes of the different templates
I hope you catch the drift
0
 

Author Comment

by:phil301
ID: 24199918
Thanks for the quick replys. I will try this when I get back to the office in the morning.

The actual XML data is confidential data, but I should be able to modify it and post the data.

The XML that Getrone recreated looks correct.

Thanks again for the quick replys, I will get back with you in the morning.
0
 

Author Comment

by:phil301
ID: 24199936
By the way, this is my first experience with XML ,
0
 
LVL 39

Expert Comment

by:abel
ID: 24199963
> seems like your example would have gone in the same direction anyway

yes, about, but you're quite a bit quicker with coming up with a good example. You never sleep? lol ;-)
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24199979
oh, I slept one hour Sunday afternoon prior to watching a bit of the Amstel Gold Race,
and then, if I remember well, I must have slept an hour or two in January :-)
0
 

Author Comment

by:phil301
ID: 24204186
I tried to insert the XSL code above and get the following error:


The XML page cannot be displayed
Cannot view XML input using XSL style sheet. Please correct the error and then click the Refresh button, or try again later.


--------------------------------------------------------------------------------

Invalid at the top level of the document. Error processing resource 'file:///C:/XMLExport/xmlstyle.xslt'. Line 40, Positio...

</xsl:stylesheet>

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24204264
When copying the XSLT from the edit field,
you did not by any chance copy the words
"Open in New Window Select All" after the stylesheet as well,
it happens to me all the time
make sure that the last string in your XSLT is the closing tag for xsl:stylesheet
0
 

Author Comment

by:phil301
ID: 24204373
Ok, getting closer. Not sure what the problem above was.
I am able to run it now, but I am still only getting top level parts, and an extra column on the right that has the text "Top" in it.
0
 

Author Comment

by:phil301
ID: 24204388
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
   
here is the XSL I am using now.


xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    >
    <xsl:template match="/">
        <html>
            <body>
                <h2>My Parts</h2>
                <table border="1">
                    <tr bgcolor="#9acd32">
                        <th>ID</th>
                        <th>Description</th>
                        <th>Parent</th>
                    </tr>
                    <xsl:apply-templates

select="SUBPARTS/PART"></xsl:apply-templates>
                </table>
            </body>
        </html>
    </xsl:template>
   
    <xsl:template match="PART">
        <tr>
            <td><xsl:value-of select="ID"/></td>
            <td><xsl:value-of
                select="Description"/></td>
           
          <td><xsl:value-of
                select="Parent"/></td>
            <td>
                <xsl:choose>
                    <xsl:when

test="ancestor::PART">
                        <xsl:value-of

select="ancestor::PART[1]/ID"/>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:text>TOP</xsl:text>
                    </xsl:otherwise>
                </xsl:choose>
            </td>
        </tr>
        <xsl:apply-templates

select="SUBPART/PART"></xsl:apply-templates>
    </xsl:template>
   
</xsl:stylesheet>
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24204405
Try first with my example XML
Then see what is different
Preferably, take a part from your real XML and twiddle a bit with the data
If you wish, I can send you an XSLT that leaves the structure intact, but changes every character in a *
0
 

Author Comment

by:phil301
ID: 24204440
Here is the actual XML with data X'ed out

<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="xmlstyle.xslt"?>
<SUBPARTS>
      <PART>
            <ID>XXXXXXX</ID>
            <Description>XXXXXXX</Description>
            <Drawing>XXXXXXX</Drawing>
            <Parent>XXXXXXX</Parent>
            <Qty>X</Qty>
            <NSN>XXXXXXX</NSN>
      <\PART>
      <PART>
            <ID>XXXXXXX</ID>
            <Description>XXXXXXX</Description>
            <Drawing>XXXXXXX</Drawing>
            <Parent>XXXXXXX</Parent>
            <Qty>X</Qty>
            <NSN>XXXXXXX</NSN>
      <\PART>
      <PART>
            <ID>XXXXXXX</ID>
            <Description>XXXXXXX</Description>
            <Drawing>XXXXXXX</Drawing>
            <Parent>XXXXXXX</Parent>
            <Qty>X</Qty>
            <NSN>XXXXXXX</NSN>
                  <SUBPARTS>
                        <PART>
                              <ID>XXXXXXX</ID>
                              <Description>XXXXXXX</Description>
                              <Drawing>XXXXXXX</Drawing>
                              <Parent>XXXXXXX</Parent>
                              <Qty>X</Qty>
                              <NSN>XXXXXXX</NSN>
                        <\PART>
                        <PART>
                              <ID>XXXXXXX</ID>
                              <Description>XXXXXXX</Description>
                              <Drawing>XXXXXXX</Drawing>
                              <Parent>XXXXXXX</Parent>
                              <Qty>X</Qty>
                              <NSN>XXXXXXX</NSN>
                        <\PART>
                  <\SUBPARTS>
      <\PART>
      <PART>
            <ID>XXXXXXX</ID>
            <ID>XXXXXXX</ID>
            <Description>XXXXXXX</Description>
            <Drawing>XXXXXXX</Drawing>
            <Parent>XXXXXXX</Parent>
            <Qty>X</Qty>
            <NSN>XXXXXXX</NSN>
      <\PART>
      <PART>
            <ID>XXXXXXX</ID>
            <Description>XXXXXXX</Description>
            <Drawing>XXXXXXX</Drawing>
            <Parent>XXXXXXX</Parent>
            <Qty>X</Qty>
            <NSN>XXXXXXX</NSN>
      <\PART>
<\SUBPARTS>
0
 
LVL 60

Accepted Solution

by:
Geert Bormans earned 1200 total points
ID: 24204528
the problem is close to the end of your stylesheet
there is a SUBPART that should be SUBPARTS
       <xsl:apply-templates 
 
select="SUBPARTS/PART"></xsl:apply-templates>
    </xsl:template>
    
</xsl:stylesheet>

Open in new window

0
 

Author Comment

by:phil301
ID: 24204660
You sir are a genius. That works great. I just now have to work on indentation of the children and a way to print it out in the indented structure.



Thank you so much for your help.
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 24204673
welcome
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.

Question has a verified solution.

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

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.
Create a Windows 10 custom Image with custom task bar and custom start menu using XML for deployment.
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:
The viewer will learn how to look for a specific file type in a local or remote server directory using PHP.
Suggested Courses

850 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