Subject: RE: [xsl] Advice/feedback on stylesheet? From: "M. David Peterson" <m.david@xxxxxxxxxx> Date: Sat, 27 Mar 2004 09:17:39 -0700 |
Well, It just so happens that I am currently in the documentation process of a project I just finished and have been keeping my eye out for projects on the list that can act as good tutorial material for the "concepts of XSLT and functional programming" section of my docs. I don't know if you should be excited or frightened (I tend to get a little "windy" sometimes in my docs, but for good purpose.. I want to make sure that all the information is available to showcase and help answer questions as to the process of how and why something works and not just leave it up to the end user to figure out for him or her self.) but I saw your posting this morning and decided to use what will soon be the not so quiet hours of a Saturday morning here in the Rocky Mountains to drink my coffee and create a tutorial that solutions your project into an 100% functional based process flow that uses temporary tree structures to add a layer less of flatness to your data which then allows for the beauty of the naturally occurring recursive process contained in XSL and other functional programming languages to showcase it stuff. Ive commented the entire stylesheet so I will just leave it up to my comments to explain the process. Please, if you or anybody has comments or questions please let me know. My desire is to create self teaching tutorials that assume just enough knowledge of functional programming to make the reader dangerous and yet not so technical that even the processor has a hard time understand what I'm trying to say/do. As such, I give you version 0.1 of "the flat-file recursive rebuild project". Enjoy! Oh, by the way... I would copy and paste the text of this into an editor of some sort as I didn't use any line breaks in my comments. Depending on your page width and if you have word wrap on the following code could look more like the decompiled code of a 16-bit Windows EXE than a printy-printed XML-based document structure. My purpose for using this particular "comment right after the associated code element that goes on forever" is that I have another template that takes the structure of the xsl:stylesheet and creates a CSS/HTML tree structure that allows access to each related comment by simply clicking on the node representation of the xsl:element in the tree structure. I have a site under development (still under heavy wraps as it is a fairly extensive effort that I want to showcase all at once and not in bits and pieces) that will allow access to these tools and templates to allow for much easier reading and therefore understanding of the content using this documentation technique. Stay in tune to the list for further details in the not so distant future. Best of luck! This XSL uses your original XML data structure which I left intact at the bottom of this email for others to play with if they desire. <M:D/> <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xalan="http://xml.apache.org/xalan" version="1.0"> <xsl:output method="xml" indent="yes"/> <xsl:template match="/"><!-- NOTE: match root of XML Doc --> <xsl:element name="ROWSET"><!-- NOTE: Create ROWSET Element for output --> <xsl:apply-templates select="//NODE[LEVEL_ = '1']" mode="group"><!-- NOTE: apply-templates to just to only the NODE elements have a value 0f '1' for the LEVEL_ attribute and use the mode attribute set to group --> <xsl:sort select="LEVEL_"/><!-- NOTE: Sort by the LEVEL_ element to ensure we get the results in the proper order. OBVIOUSLY this particular case doesnt require us to sort them given that we are only matching to "LEVEL_" values of 1 in our XPath statement. But, for the sake of good practice (if the criteria every changes then the sorting is already in place) I put it in there. --> <xsl:with-param name="allElements" select="*"/><!-- NOTE: pass the parameter all element, seleceting all the elements in the XML Doc --> </xsl:apply-templates> </xsl:element> </xsl:template> <xsl:template match="NODE" mode="group"><!-- NOTE: this template matches all "NODE" elements that were called from apply-templates that used the mode attribute of "group" --> <xsl:param name="allElements"/><!-- NOTE: create parameter that will be set to the value passed by the template --> <xsl:variable name="SRCD" select="SRCD"/><!-- NOTE: create SCRD variable for each NODE element that passes through this template. This will be used to compare to the first character of all the other NODE elements for grouping --> <xsl:variable name="groups"><!-- NOTE: create a variable named group that holds the Result Tree Fragment of the grouping process --> <xsl:element name="group"><!-- NOTE: create element "group" that will the hold the contents of each distinct group that is passed from the origination template --> <xsl:apply-templates select="$allElements/NODE[starts-with(SRCD, $SRCD)]" mode="groupTree"><!-- NOTE: apply-templates using the allElements parameter passed from the previous template. This parameter contains and RTF of all the elements and attributes in the XML Doc. The processor will look for the first element in the RTF created by the XPath in the select attribute and match it to the first template that contains the proper element in the match attribute and the mode attribute set to "groupTree" --> <xsl:sort select="LEVEL_"/><!-- NOTE: sort using the "LEVEL_" value element --> <xsl:sort select="translate(SRCD, ',', '')"/><!-- NOTE: then sort on the translated value of the SRCD element (this removes the commas so the processor can sort them as standard integers. This isnt always necessary but its good practice to ensure accurate results EVERY time --> </xsl:apply-templates> </xsl:element> </xsl:variable> <xsl:apply-templates select="xalan:nodeset($groups)" mode="groupTreeBranches"/><!-- NOTE: use the nodeset function of your particular processor (dont forget to declare the namespace extension in your xsl:stylesheet decleration to convert the RTF created by the above apply-templates process --> </xsl:template> <xsl:template match="NODE" mode="groupTree"><!-- NOTE: the above template matches each NODE elements that matched the grouping criteria created in the above templates. In this case it took each NODE that had a "LEVEL_" element value of '1'. The next template matched the first character in the "SRCD" element of every node with the 4 elements that matched the previous templates rules. So in this case the first template that matched --> <xsl:element name="NODE"><!-- NOTE: It the creates a NODE element in the output stream --> <xsl:copy-of select="child::*"/><!-- NOTE: and then copys all the elements contained within the originating NODE in the output stream as well --> </xsl:element><!-- once this process has completed (all 4 original matching elements are grouped properly with there respective elements) the second apply-templates will be called, parsing through the RTF created by this process matching (to the specified XPath) each element contained within the direct document flow (meaning the sibling elements that were matched with the XPath statement). To access any elements contained within these sibling elements will require another apply-templates call from the next template (if recursive behavior is required. Given that the XML Doc file started as a flat file doesnt allow for a simple example of the recursive nature of apply-templates but the process has at least given us four distinct groups to work with now which will allow some recursion processing to make our job easier. --> </xsl:template> <xsl:template match="group" mode="groupTreeBranches"><!-- NOTE: the template before this one created a nodeset from a RTF from the prior process and has now began matching its elements to this template --> <xsl:apply-templates select="NODE[LEVEL_ = 1]" mode="groupTreeBranches"/><!-- NOTE: this template simply takes, once again, our "NODE" elements that have a "LEVEL_" value of 1. This will allow us to then use recursion to access the remaining elements in the group and reunite them with there long lost parent and potential siblings --> </xsl:template> <xsl:template match="NODE" mode="groupTreeBranches"><!-- NOTE: the above template matches to this template (again, using match and mode to ensure we are accessing the template intended for this particular pass through the NODE elements--> <xsl:variable name="LEVEL_" select="LEVEL_"/><!-- NOTE: a "LEVEL_" variable is created to be used for comparisons in our parent/child grouping process about to take place --> <xsl:variable name="SRCD" select="SRCD" /><!-- NOTE: again, another comparison variable is created, this time the value of the "SCRD" element is set to the variable name "SCRD" (this doesnt create any problems in our XPath because we access variables using the $ sign in front telling our processor to access the variable value and not the element value --> <xsl:element name="NODE"><!-- NOTE: again, another NODE element is created. However this one will be a permanent fixture in our final output where as our previous NODE element was created as a temporary, yet structured, place holder of the values we want to access is this processing of the final output --> <xsl:element name="SRCD"><!-- NOTE: a "SRCD" element is created in our output tree and the value set to the value of the current NODE element in context --> <xsl:value-of select="SRCD"/><!-- NOTE: value is set using xsl:value-of to access the content found between the opening and closing SRCD tags --> </xsl:element> <xsl:copy-of select="child::*[contains(name(), 'ELEMENT')]"/><!-- this process makes a copy of all child elements of our context node (in this case literally NODE) that match the XPath test that using the contains function and uses the name() function as the test to see if it contains the sequence of characters "ELEMENT". If yes, it makes a copy of the entire element and associated values to the output. If no, it continues testing each child element until there are no more to test. Please note that this particular XPath test didnt include any reference to text() or comment() (which are technically text() nodes) nodes and as such any contained within the child structure of the containing NODE element would not be tested by the processor. --> <xsl:element name="CHILDREN"><!-- NOTE: heres where we create our CHILDREN element and the use XPath tests and recursion to process the remaining NODE elements and place them with there proper parent and respective siblings --> <xsl:apply-templates select="following-sibling::NODE[LEVEL_ = $LEVEL_ + 1 and substring(SRCD, 1, string-length($SRCD)) = $SRCD]" mode="groupTreeBranches"/><!-- NOTE: this apply-templates uses "following-sibling" as its axis for testing whether or not the next node is a child of any prior NODE elements. Two tests are performed and both must pass for this element to become a child or sibling element to the current NODE element in process. The first adds a value of "1" to the "LEVEL_" variable we created earlier in the process and then tests to see if the value of this NODEs "LEVEL_" element is equal. If it is then we know that this is a potential candidate as a child node. But were not done yet. Our XPath statement used the following-sibling axis with no designation to position so our processor is checking EVERY following sibling to see if its "LEVEL_" value is = the "LEVEL_" variable + 1. Given that it is very likely that somewhere along the way a "LEVEL_" element contained in a following NODE element will have a distance of levels deep within the document that is the same as the "LEVEL_" variable + 1 then this test alone will not be adequate. Our next test uses the string-length of the "SRCD" element of the first preceding-sibling NODE element whos "LEVEL_" value is exactly one less than that of the NODE in question and then uses that to trim the length of the current CDFS element and then compares the two. If this preceding-element is truly the parent of this element in question then their "genetic" sequence should match. For example, if the previous matching element had a value of 3,1 for SRCD then for our element to be a child he/she too would have to have 3,1 as his/her first two values. If he/she does, WHAMO, a family had bee reunited. If not, the quest (at least for this NODE element) to find his long lost family members must continue during the next pass through yet one more temporary foster home--> </xsl:element><!-- every match made using the XPath test above will be processed within the context of this "CHILDREN" element. Given that we use the same match element ("NODE") and the same mode ("groupTreeBranches") as the xsl:template it is contained in then each matching node will also go through this quest to find there long lost children. As such this template has created a recursive process that will continue without hesitation or concern for anything but bringing back together each familys mom, dad, father, son, mother, daughter, sister, brother, grandm.... ok, ill stop ;) --> </xsl:element> </xsl:template> </xsl:stylesheet> -----Original Message----- From: Jim Stoll [mailto:jestoll@xxxxxxxxxx] Sent: Friday, March 26, 2004 10:06 PM To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: [xsl] Advice/feedback on stylesheet? 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"><</xsl:text><xsl:value-of select="$root-tag-param"/><xsl:text disable-output-escaping="yes">></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"></</xsl:text><xsl:value-of select="$root-tag-param"/><xsl:text disable-output-escaping="yes">></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 |
---|
|
<- Previous | Index | Next -> |
---|---|---|
RE: [xsl] Advice/feedback on styles, Andreas L. Delmelle | Thread | RE: [xsl] Advice/feedback on styles, Jim Stoll |
RE: [xsl] Using keys when unique id, M. David Peterson | Date | [xsl] this is only a test, lech |
Month |