xsl-list
[Top] [All Lists]

Advice/feedback on stylesheet?

2004-03-26 22:05:43
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 eac
 h 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>


<Prev in Thread] Current Thread [Next in Thread>