Link to home
Start Free TrialLog in
Avatar of phil301
phil301

asked on

XSL Tree for-each

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>
Avatar of abel
abel
Flag of Netherlands image

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.
Avatar of Gertone (Geert Bormans)
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
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?
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

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

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
Avatar of phil301
phil301

ASKER

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.
Avatar of phil301

ASKER

By the way, this is my first experience with XML ,
> 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 ;-)
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 :-)
Avatar of phil301

ASKER

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>

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
Avatar of phil301

ASKER

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.
Avatar of phil301

ASKER

<?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>
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 *
Avatar of phil301

ASKER

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>
ASKER CERTIFIED SOLUTION
Avatar of Gertone (Geert Bormans)
Gertone (Geert Bormans)
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of phil301

ASKER

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.