[XSL-LIST Mailing List Archive Home] [By Thread] [By Date]

[xsl] Advice/feedback on stylesheet?


Subject: [xsl] Advice/feedback on stylesheet?
From: "Jim Stoll" <jestoll@xxxxxxxxxx>
Date: Sat, 27 Mar 2004 00:05:43 -0500

All,
I'm an XSL Newbie again (last used it about 2 years ago).  Anyway, I'm looking for some review/feedback on a relatively simple stylesheet.  As background, I'm executing an SQL query against an Oracle database, using Oracle's CONNECT BY functionality to bring back a hierarchical result set. I'm then transforming the results into XML via Oracle 9.1's SYS_XMLAGG and SYS_XMLGEN functionality (largely unimportant, but I mention it in case it helps anyone), which brings me back a 'flat' XML representation of the previously-hierarchical result set. (This version of Oracle isn't smart enough to maintain the hierarchical nature of the data through the XML conversion - newer versions are, but this is the version I have to use...) So, I end up w/ XML elements that are in the proper 'order', but not nested. The XSL stylesheet then nests things appropriately based on ROWID values (unique id's for each row in the resultset) for each node and its parent in the hierarchical relationship, as!
  well as a LEVEL attribute returned from the query.  (XML, XSL and output are included below).

Anyway, I'd like to know if/how I can do this in a smarter manner, if I've made any serious mis-steps or such, if this approach is likely to bring the machine to its knees for large datasets, etc. (this will be occurring on the database server).

Other specific questions:
 - thoughts on having two templates for the nodes to separate out top-level nodes, or if one template w/ an xsl:if would be better
 - the prevailing philosophy as regards whether to have an empty CHILDREN element (see below) if there are no children or not
 - I've tried to make this as generic as possible, as I have opportunity to apply it to numerous situations, but have been unable to find a way to parameterize the ROWSET_, ROWID_ and PARENTROWID_ tag names (ie, to allow the user to set these to another value in their database query and pass parameters to the stylesheet naming their substituted values) - if anyone has any thoughts on this, I'd be very interested to hear them, as well. (It works as-is, and is an acceptable work-around, but I'd at least like to know if its possible.)

All advice is appreciated! (and yes, lets establish up-front, I'm an idiot... That's why I'm here... <:-)

Thanks Very Much!!

Jim Stoll

<<input XML>>
<?xml version="1.0"?>
<ROWSET>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAG</ROWID_>
  <SRCD>1</SRCD>
  <ELEMENT1>1e1</ELEMENT1>
  <ELEMENT2>1e2</ELEMENT2>
  <ELEMENT3>1e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAH</ROWID_>
  <SRCD>2</SRCD>
  <ELEMENT1>2e1</ELEMENT1>
  <ELEMENT2>2e2</ELEMENT2>
  <ELEMENT3>2e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAI</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAH</PARENTROWID_>
  <SRCD>2,1</SRCD>
  <ELEMENT1>21e1</ELEMENT1>
  <ELEMENT2>21e2</ELEMENT2>
  <ELEMENT3>21e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAJ</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAI</PARENTROWID_>
  <SRCD>2,1,1</SRCD>
  <ELEMENT1>211e1</ELEMENT1>
  <ELEMENT2>211e2</ELEMENT2>
  <ELEMENT3>211e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAK</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAI</PARENTROWID_>
  <SRCD>2,1,2</SRCD>
  <ELEMENT1>212e1</ELEMENT1>
  <ELEMENT2>212e2</ELEMENT2>
  <ELEMENT3>212e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAL</ROWID_>
  <SRCD>3</SRCD>
  <ELEMENT1>3e1</ELEMENT1>
  <ELEMENT2>3e2</ELEMENT2>
  <ELEMENT3>3e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAM</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAL</PARENTROWID_>
  <SRCD>3,1</SRCD>
  <ELEMENT1>31e1</ELEMENT1>
  <ELEMENT2>31e2</ELEMENT2>
  <ELEMENT3>31e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAN</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAL</PARENTROWID_>
  <SRCD>3,2</SRCD>
  <ELEMENT1>32e1</ELEMENT1>
  <ELEMENT2>32e2</ELEMENT2>
  <ELEMENT3>32e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAO</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAN</PARENTROWID_>
  <SRCD>3,2,1</SRCD>
  <ELEMENT1>321e1</ELEMENT1>
  <ELEMENT2>321e2</ELEMENT2>
  <ELEMENT3>321e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>2</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAP</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAL</PARENTROWID_>
  <SRCD>3,3</SRCD>
  <ELEMENT1>33e1</ELEMENT1>
  <ELEMENT2>33e2</ELEMENT2>
  <ELEMENT3>33e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>3</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAQ</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAP</PARENTROWID_>
  <SRCD>3,3,1</SRCD>
  <ELEMENT1>331e1</ELEMENT1>
  <ELEMENT2>331e2</ELEMENT2>
  <ELEMENT3>331e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>4</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAR</ROWID_>
  <PARENTROWID_>AAAMDnAAPAAAEHuAAQ</PARENTROWID_>
  <SRCD>3,3,1,1</SRCD>
  <ELEMENT1>3311e1</ELEMENT1>
  <ELEMENT2>3311e2</ELEMENT2>
  <ELEMENT3>3311e3</ELEMENT3>
</NODE>
<NODE>
  <LEVEL_>1</LEVEL_>
  <ROWID_>AAAMDnAAPAAAEHuAAS</ROWID_>
  <SRCD>4</SRCD>
  <ELEMENT1>4e1</ELEMENT1>
  <ELEMENT2>4e2</ELEMENT2>
  <ELEMENT3>4e3</ELEMENT3>
</NODE>
</ROWSET>

<<XSL>>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">

	<!--
		Preconditions on the database-generated XML processed by this stylesheet:
			- there will always be a root element named ROWSET, that is not part of the 'data' hierarchy (ie, its part of the XML hierarchy, but not part of the original relational data hierarchy)
			- all 'data' elements will be siblings underneath the root element (result of the 'flattening' nature of the database's result-set-to-XML conversion)
			- every 'data' element will have a child 'utility' element named LEVEL_ that indicates the nesting level in the original data hierarchy, where 1 is a top-level element (ie, no parent in the data hierarchy)
			- every 'data' element will have a child  'utility' element named ROWID_ that is a unique identifier for the row in the original data hierarchy
			- every data element that is a child of another data element will have a child  'utility' element named PARENTROWID_ that identifies the data element's parent
	-->
						
	<!--while database-generated XML will always be contained in a root node named ROWSET, the user can choose whether to include a root tag in the output, and if so, what that tag will be named - defaults to ROWSET-->
	<xsl:param name="root-tag-param">ROWSET</xsl:param>
	
	<!--if a node has children, they will be grouped in a wrapper tag, but the user can specify the wrapper tag name if desired - defaults to CHILDREN-->
	<xsl:param name="children-tag-param">CHILDREN</xsl:param>
	
	<!--by default, the mandatory LEVEL_ and ROWID_ tags provided in the database-generated XML (and required for this stylesheet's operations) will not be included in the output, but can be if desired-->
	<xsl:param name="output-level-tag-param">false</xsl:param>
	<xsl:param name="output-rowid-tag-param">false</xsl:param>
	<xsl:param name="output-parent-rowid-tag-param">false</xsl:param>

	<!--recall precondition that the database-generated XML will always have a root element named ROWSET, and that all 'data' elements will be siblings under the root, so map all nodes to their parent-->
	<xsl:key name="key-nodes-by-parent" match="/ROWSET/*" use="PARENTROWID_"/>
	
	<xsl:template match="/">
		<!--if a root tag value is provided, use it,otherwise, just produce a fragment-->
		<xsl:if test="$root-tag-param!=''">
			<xsl:text disable-output-escaping="yes">&lt;</xsl:text><xsl:value-of select="$root-tag-param"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
		</xsl:if>
			<!--always want to process the document though, regardless of whether a root tag is to be output or not!-->
			<xsl:apply-templates/>
		<xsl:if test="$root-tag-param!=''">
			<xsl:text disable-output-escaping="yes">&lt;/</xsl:text><xsl:value-of select="$root-tag-param"/><xsl:text disable-output-escaping="yes">&gt;</xsl:text>
		</xsl:if>
	</xsl:template>
	
	<!--all top-level parents will have a level of 1 - only match/process these, as they will in turn process their children via the key map-->
	<xsl:template match="*[LEVEL_='1']">
		<xsl:call-template name="duplicate-node">
			<xsl:with-param name="duplicatee-param" select="."/>
		</xsl:call-template>
	</xsl:template>
	
	<!--don't match children(any node w/ a level != 1) - as they'll be processed by their parent-->
	<xsl:template match="*[LEVEL_!='1']">
	</xsl:template>
	
	<!--for each node - either top-level parents resulting from a match, or children resulting from key iteration - duplicate the node, its children and attributes-->
	<xsl:template name="duplicate-node">
		<xsl:param name="duplicatee-param"/>
		<xsl:copy>
			<xsl:copy-of select="@*"/>
			<!--output  the 'utility' elements if/as specified by params-->
			<xsl:copy-of select="*[(not(self::thisElement)) and (not(name()='LEVEL_') or (name()='LEVEL_' and $output-level-tag-param='true')) and (not(name()='ROWID_') or (name()='ROWID_' and $output-rowid-tag-param='true')) and (not(name()='PARENTROWID_') or (name()='PARENTROWID_' and $output-parent-rowid-tag-param='true'))]"/>
			<xsl:element name="{$children-tag-param}">
				<xsl:for-each select="key('key-nodes-by-parent', ROWID_)">
					<xsl:call-template name="duplicate-node">
						<xsl:with-param name="duplicatee-param" select="."/>
					</xsl:call-template>
				</xsl:for-each>
			</xsl:element>
		</xsl:copy>
	</xsl:template>
</xsl:stylesheet>

<<output XML>>
<?xml version="1.0" encoding="UTF-8"?>
<ROWSET>
	<NODE>
		<SRCD>1</SRCD>
		<ELEMENT1>1e1</ELEMENT1>
		<ELEMENT2>1e2</ELEMENT2>
		<ELEMENT3>1e3</ELEMENT3>
		<CHILDREN />
	</NODE>
	<NODE>
		<SRCD>2</SRCD>
		<ELEMENT1>2e1</ELEMENT1>
		<ELEMENT2>2e2</ELEMENT2>
		<ELEMENT3>2e3</ELEMENT3>
		<CHILDREN>
			<NODE>
				<SRCD>2,1</SRCD>
				<ELEMENT1>21e1</ELEMENT1>
				<ELEMENT2>21e2</ELEMENT2>
				<ELEMENT3>21e3</ELEMENT3>
				<CHILDREN>
					<NODE>
						<SRCD>2,1,1</SRCD>
						<ELEMENT1>211e1</ELEMENT1>
						<ELEMENT2>211e2</ELEMENT2>
						<ELEMENT3>211e3</ELEMENT3>
						<CHILDREN />
					</NODE>
					<NODE>
						<SRCD>2,1,2</SRCD>
						<ELEMENT1>212e1</ELEMENT1>
						<ELEMENT2>212e2</ELEMENT2>
						<ELEMENT3>212e3</ELEMENT3>
						<CHILDREN />
					</NODE>
				</CHILDREN>
			</NODE>
		</CHILDREN>
	</NODE>
	<NODE>
		<SRCD>3</SRCD>
		<ELEMENT1>3e1</ELEMENT1>
		<ELEMENT2>3e2</ELEMENT2>
		<ELEMENT3>3e3</ELEMENT3>
		<CHILDREN>
			<NODE>
				<SRCD>3,1</SRCD>
				<ELEMENT1>31e1</ELEMENT1>
				<ELEMENT2>31e2</ELEMENT2>
				<ELEMENT3>31e3</ELEMENT3>
				<CHILDREN />
			</NODE>
			<NODE>
				<SRCD>3,2</SRCD>
				<ELEMENT1>32e1</ELEMENT1>
				<ELEMENT2>32e2</ELEMENT2>
				<ELEMENT3>32e3</ELEMENT3>
				<CHILDREN>
					<NODE>
						<SRCD>3,2,1</SRCD>
						<ELEMENT1>321e1</ELEMENT1>
						<ELEMENT2>321e2</ELEMENT2>
						<ELEMENT3>321e3</ELEMENT3>
						<CHILDREN />
					</NODE>
				</CHILDREN>
			</NODE>
			<NODE>
				<SRCD>3,3</SRCD>
				<ELEMENT1>33e1</ELEMENT1>
				<ELEMENT2>33e2</ELEMENT2>
				<ELEMENT3>33e3</ELEMENT3>
				<CHILDREN>
					<NODE>
						<SRCD>3,3,1</SRCD>
						<ELEMENT1>331e1</ELEMENT1>
						<ELEMENT2>331e2</ELEMENT2>
						<ELEMENT3>331e3</ELEMENT3>
						<CHILDREN>
							<NODE>
								<SRCD>3,3,1,1</SRCD>
								<ELEMENT1>3311e1</ELEMENT1>
								<ELEMENT2>3311e2</ELEMENT2>
								<ELEMENT3>3311e3</ELEMENT3>
								<CHILDREN />
							</NODE>
						</CHILDREN>
					</NODE>
				</CHILDREN>
			</NODE>
		</CHILDREN>
	</NODE>
	<NODE>
		<SRCD>4</SRCD>
		<ELEMENT1>4e1</ELEMENT1>
		<ELEMENT2>4e2</ELEMENT2>
		<ELEMENT3>4e3</ELEMENT3>
		<CHILDREN />
	</NODE>
</ROWSET>


Current Thread
Keywords