[XSL-LIST Mailing List Archive Home] [By Thread] [By Date] [Recent Entries] [Reply To This Message] sort a node across a dynamic group of files
Hello-- I searched the archives of this forum (and the internet at large) looking for an answer to this query before posting, but I apologize in advance if I missed something. I found lots of results that seem to work if you have a static (and known) list of files before starting. If you have any suggestions for better search terms, please let me know. I am using XSLT 1.0. I perform the transformation using PHP 5.3.0 / Xalan 2.4.1 on an Apache 2.2.13 server. Unfortunately I do not have admin access to Apache to do any kind of upgrades that would allow me to use XSLT 2.0. Here's more context than you could ever want: I have multiple XML files in a directory. The number of files is frequently growing but they are all named ####.xml (example: 0001.xml, 0002.xml, 0003.xml, etc.) It is important to know that there will *never* be any skipped numbers. If 0100.xml exists it is guaranteed that 0001-0099 exist as well. The relevant structure of these files is as follows: <!-- Entry XML --> <xs:element name="entry"> <xs:complexType> <xs:sequence> <xs:element ref="id" /> <xs:choice> <xs:group ref="name" /> <xs:element ref="name" /> </xs:choice> [ ... ] </xs:sequence> </xs:complexType> </xs:element> And the elements / group referenced: <!-- Elements --> <xs:element name="firstName" type="xs:token" /> <xs:element name="id" type="xs:integer" /> <xs:element name="lastName" type="xs:token" /> <xs:element name="middleName" type="xs:token" /> <xs:element name="name" type="xs:token" /> <!-- Groups --> <xs:group name="name"> <xs:sequence> <xs:element ref="firstName" /> <xs:element ref="middleName" minOccurs="0" maxOccurs="unbounded" /> <xs:element ref="lastName" minOccurs="0" /> </xs:sequence> </xs:group> There are never more than one entry in a given file. I use a schema to validate that this is true. I keep track of how many entry files there are through a PHP script that runs periodically and updates a master XML file with the current count of entry XML files. The relevant structure of this master XML file is as follows: <!-- TOC XML --> <xs:element name="toc"> <xs:complexType> <xs:sequence> [...] <xs:element ref="entries" /> </xs:sequence> </xs:complexType> </xs:element> And the relevant element definition: <!-- Elements --> <xs:element name="entries" type="xs:integer" /> I have found elsewhere a method where I can dynamically include any number of files using recursive calls to the same template: <!-- Method --> <xsl:template match="example"> <!-- Set up our iterators --> <xsl:param name="i" select="document( 'toc.xml' )//entries" /> <!-- Loop through the matches --> <xsl:for-each select="document( concat( format-number( $i, '0000' ), '.xml' ) )"> <!-- Whatever XSLT work I need done --> </xsl:for-each> <!-- If there are still items to loop through execute another iteration --> <xsl:if test="$i > 1"> <!-- Apply the template again --> <xsl:apply-templates select="."> <xsl:with-param name="i" select="$i - 1" /> </xsl:apply-templates> </xsl:if> </xsl:template> And this has worked great for me for other tasks. However, when I need to sort the items I'm pulling from the external XML files this doesn't seem to work. I get returned the proper information but in the exact order of the files (0001 then 0002 then 0003 then 0004) regardless of the sort request. The reason I use separate XML files and not one big XML file with all the entries in them is because when you click through the TOC to an entry you only view one at a time. I cannot use $_REQUEST parameters to specify which record to display due to the restrictions of my server set-up. All explanations I've seen that detail how to start using $_REQUEST parameters in your XSLT require additional installations that I do not have and my inability to edit/access my apache server prevents me from installing them. So for me to only display record #5 I have to store the records separately and then pull up 0005.xml and process it. Here is an example of the TOC XML file: <?xml version="1.0" encoding="utf-8" ?> <toc> [...] <entries>15</entries> </toc> And a couple of examples of the entry XML files: <!-- 0001.xml --> <?xml version="1.0" encoding="utf-8" ?> <entry> <id>1</id> <firstName>First</firstName> <lastName>Last</lastName> [...] </entry> <!-- 0002.xml --> <?xml version="1.0" encoding="utf-8" ?> <entry> <id>2</id> <name>Name of Place</name> [...] </entry> So. Now that you have lots of context. A direct summary of my problem: 1) The only way I can seem to get and sort all the information is if I hardcode in variables for every external file and then do a massive for-each across them. This is highly undesirable because the number of entry XML files is assumed to soon be in the thousands. I know that I can modify the PHP script that updates the "entries" section of the TOC XML to also add lines to the XSL file but I figure there has to be a more efficient way to do this. Here's the current version of the XSL file I have for this process: <?xml version="1.0" encoding="utf-8" ?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- Set our XHTML 1.0 Strict doctype with no indenting --> <xsl:output method="xml" indent="no" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" /> [...] <!-- Table of Contents --> <xsl:template match="toc"> [...] <!-- The links --> <xsl:element name="ul"> <xsl:apply-templates select="entries" /> </xsl:element> </xsl:template> <!-- Entries --> <xsl:template match="entries"> <!-- Variables --> <xsl:variable name="v1" select="document( '0001.xml' )//entry" /> <xsl:variable name="v2" select="document( '0002.xml' )//entry" /> <xsl:variable name="v3" select="document( '0003.xml' )//entry" /> <xsl:variable name="v4" select="document( '0004.xml' )//entry" /> <!-- and so on --> <!-- Loop through the variables --> <xsl:for-each select="$v1|$v2|$v3|$v4"> <!-- and so on --> <!-- Sort --> <xsl:sort select="name|firstName" /> <!-- The list item --> <xsl:element name="li"> <!-- My XSL processing --> </xsl:element> </xsl:for-each> </xsl:template> </xsl:stylesheet> This actually works. But as I said earlier, maintaining this looks to be a bear. An earlier plan was to try and use the recursive method above. That looked like this: <!-- Just the relevant entry template --> <xsl:template match="entries"> <!-- Set up our iterators --> <xsl:param name="i" select="document( 'toc.xml' )//entries" /> <!-- Loop through the matches --> <xsl:for-each select="document( concat( format-number( $i, '0000' ), '.xml' ) )"> <!-- Sort --> <xsl:sort select="name|firstName" /> <!-- The list item --> <xsl:element name="li"> <!-- My XSL processing --> </xsl:element> </xsl:for-each> <!-- If there are still items to loop through execute another iteration --> <xsl:if test="$i > 1"> <!-- Apply the template again --> <xsl:apply-templates select="."> <xsl:with-param name="i" select="$i - 1" /> </xsl:apply-templates> </xsl:if> </xsl:template> But this failed. It just showed them in the same order that they exist in the files. Thank you for any help you can give on this matter. I appreciate any and all leads. I hope I haven't left any relevant information out. Cheers, -J -- J. Argyl Plath :-{) I'm wearing a moustache to help fight prostate cancer. Learn more and help out at: http://us.movember.com/mospace/24373
|
PURCHASE STYLUS STUDIO ONLINE TODAY!Purchasing Stylus Studio from our online shop is Easy, Secure and Value Priced! Download The World's Best XML IDE!Accelerate XML development with our award-winning XML IDE - Download a free trial today! Subscribe in XML format
|