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>
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>
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
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>
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
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>
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
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
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.
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.
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 ;-)
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 :-)
and then, if I remember well, I must have slept an hour or two in January :-)
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/xmls tyle.xslt' . Line 40, Positio...
</xsl:stylesheet>
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/xmls
</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
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
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.
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.
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"></x sl:apply-t emplates>
</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"></xs l:apply-te mplates>
</xsl:template>
</xsl:stylesheet>
<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"></x
</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]/
</xsl:when>
<xsl:otherwise>
<xsl:text>TOP</xsl:text>
</xsl:otherwise>
</xsl:choose>
</td>
</tr>
<xsl:apply-templates
select="SUBPART/PART"></xs
</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 *
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 *
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</Desc ription>
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc ription>
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc ription>
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<SUBPARTS>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc ription>
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc ription>
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<\SUBPARTS>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc ription>
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc ription>
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<\SUBPARTS>
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="xmlstyle.xslt"?>
<SUBPARTS>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<SUBPARTS>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<\SUBPARTS>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<PART>
<ID>XXXXXXX</ID>
<Description>XXXXXXX</Desc
<Drawing>XXXXXXX</Drawing>
<Parent>XXXXXXX</Parent>
<Qty>X</Qty>
<NSN>XXXXXXX</NSN>
<\PART>
<\SUBPARTS>
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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.
Thank you so much for your help.
welcome
> 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.