Subject: RE: [xsl] FW: key, generate-id, ignoring my template From: "M. David Peterson" <m.david@xxxxxxxxxx> Date: Tue, 23 Mar 2004 00:49:57 -0700 |
If I may jump in here and try to help... Im going through your code and my head is beginning to spin. I'm confused as to why you are using keys here anyway but with more information maybe you could help me understand. With that aside the biggest problem I see with all of this that you are attempting to do to much of the work for the XSLT processor and not letting it do the work for you. So for instance, instead of going and getting every instance of the element <b> manually using XPath statements within apply-templates select attributes (e.g. /html/body/h1/b and html/body/p/b in separate xsl:apply-templates) you would simply just match the element b in your XPath statement located in the match attribute of the appropriate template like this: <xsl:template match="b"> <b> <xsl:apply-templates/> </b> </xsl:template> The job of the processor when it finds the element xsl:apply-templates is to select the Result Tree Fragment(the fragment of XML that is currently in context or select with an XPath statement) of either the current context node if you don't use a select attribute or the RTF of the XPath you entered if you do. Keep in mind that XSLT processors don't like stray text laying around so, for example, if a <b> element is matched to a template and within the matched <b> element is some text then an <i> element with some more text before both tags are closed your RTF would technically look like this: This is the text that came after the "b" element that this template just matched.<i>this is the italicized text</i> and this is the remaining text before the "b" tag closes. Since processors don't want to start the RTF with text nodes (they don't care if they end with one) it will spit out the text node wherever it happens to be at within the process then match the next element to the proper template etc... Thus, when you are dealing with this type of XML data <xsl:apply-templates/> will do both the work of <xsl:value-of select="."/> as well as its job to continue the functional processing flow. If you ever run across the problem of the same text being spit out by the processor twice you'll probably find an xsl:value-of stuck smack dab in the middle of your suspect template. This recursive apply-templates process, if used correctly, will go through the entire XML data flow element by element and match it to it's respective template for processing. So if this was your XML data: <?xml version="1.0"?> <html> <head> <title>this is the title</title> </head> <body onload="javascript: foo();"> <h1> <b>this is a bold heading 1</b> </h1> <p>this is the text of the first paragraph.<b>this is some bolded text in the first paragraph. <i>this is some bold and italicized text.</i> </b> <i>this is italicized text. </i>And this is just regular text again</p> </body> </html> And your job was to convert this XML data to HTML (disregard the fact that its technically XHTML right now - that's irrelevant in the case of recursively matching element names to a template. It works with any variation of element or attribute names you might throw at it) the first thing you would do is create your stylesheet and within the xsl:stylesheet element you would create a template with the first spot within the document you want to transverse stuck between the quotes of the match attribute. In this case "html" is the first element we want to find and then begin processing from there. So we would put "/" (meaning root) followed by "html" as the value of the match attribute ("/html"). From this point until the context node changes(e.g. within a for-each block or value-of element) we will begin our XPath statements from this point forward. Now the actual transformation from XML to HTML can begin. Something to keep in mind: xsl:apply-templates is extremely powerful when there are multiple elements nested within an XML dataset that have the same name and require the same processor output (such as <b>this is bold</b><b>so is this</b>). Its not incorrect to use it when there is only ever going to be one match but your creating an extra process that doesn't need to be there and as such slowing the transformation of your XML to HTML. None the less, if you prefer to separate the processing of all your elements in separate templates to keep your code clean there's definitely nothing wrong with that. No matter which way you choose your first template is going to look something similar to this. <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:strip-space elements="*"/> <!-- this element tells the processor to strip the excess white space out of the results of all (*) transformed elements so that our output text doesn't include all of the extra tabs and white space that tends to exist in XML files and processed as text by the processor --> <xsl:template match="/html"> <html> <head> <title><xsl:value-of select="head/title"/></title> <!-- you could also use <xsl:copy-of select="head/title"/> and accomplish the same thing. its just a matter of preference and in some cases ensuring correct processor output of HTML but that's nothing to worry about here --> <!-- if you had meta tags, and I know you do, given that there are multiple instance this would be where you put your first apply-templates with "head/meta-tags" as the value of your select attribute --> </head> <body> <xsl:copy-of select="body/@*"/> <!-- and now we take the Result Tree Fragment contained within the body tag and begin to process it with apply-templates --> <xsl:apply-templates select="body"/> </body> </html> </xsl:template> <xsl:template match="h1 | p | b | i"> <!-- now here's where the funs starts. In every case where all we want to do is copy the element name and the text following it leading up to the next tag all we have to do is join the elements within our match attribute using "|" and then use the following markup elements to output the proper name and content. We then use apply-templates to print out the text and continue the process. I added a copy-of and used select="@*" (meaning select attributes all) to pick up those stray attributes that sometimes are hanging around in the source XML. It also allows us to use this same template for other tags that have attributes that can simply be copied over without any sort of translation (e.g. <img src="foo.jpg"> would work but <image source="foo.jpg/> or other variations would not) --> <xsl:copy> <xsl:element name="name()"> <xsl:copy-of select="@*"/> <xsl:apply-templates/> </xsl:element> </xsl:copy> </xsl:template> </xsl:stylesheet> And there you have it. When the above XML is processed by the above XSLT you get this result: <html> <head> <META http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>this is the title</title> </head> <body onload="javascript: foo();"> <h1> <b>this is a bold heading 1</b> </h1> <p>this is the text of the first paragraph.<b>this is some bolded text in the first paragraph. <i>this is some bold and italicized text.</i></b><i>this is italicized text. </i>And this is just regular text again</p> </body> </html> Which is exactly what we wanted :) Obviously this is a very basic example, but by understanding how the apply-templates process is designed to work you can now begin to apply this knowledge to your stylesheets and begin to get the results you truly want. Now back to the keys... What is it that you are trying to accomplish here? It seems to me your data is already grouped correctly and as such you don't need to use keys to match element and/or attribute names and/or there respective values to other external data that potentially belongs to that particular data group. If this is the case then what you really need to do is decide within your first match statement just what data it is you want to transform and then transform it. So if your trying to transform the MEDICAL page then do this for your opening template: <xsl:template match="/form/page[@name = 'MEDICAL']"> And then change the value when you want to transform the DENTAL. If this is being transformed dynamically by a web server then simply create a variable called pageName, pass the value of the transform you want to do (e.g. "MEDICAL") to it and put that in place of the 'MEDICAL'. You can also set the value of the variable to a default value that you can either leave alone or have the process change when it begins the transformation process. Example: <xsl:variable name="pageName">MEDICAL</xsl:variable> <xsl:template match="/form/page[@name = $pageName]"> Well, I hope I have helped lead you down the path of truth and apply-templates righteousness and given you the correct keys to eternal XSLT happiness(I'm starting to get punchy; must be late ;). If I can help with anything else let me know. Have a great day! <M:D/> -----Original Message----- From: Laura Madonna [mailto:Laura.Madonna@xxxxxxxxx] Sent: Monday, March 22, 2004 5:16 PM To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: RE: [xsl] FW: key, generate-id, ignoring my template That did help, but now I am having trouble getting the children <covrg_desc> to print for each key row-plan_desc-page. I am only getting the first instance of <covrg_desc> to print for each vBenePlan. Thank you. Laura -----Original Message----- From: Andreas L. Delmelle [mailto:a_l.delmelle@xxxxxxxxxx] Sent: Monday, March 22, 2004 3:57 PM To: xsl-list@xxxxxxxxxxxxxxxxxxxxxx Subject: RE: [xsl] FW: key, generate-id, ignoring my template > -----Original Message----- > From: Laura Madonna [mailto:Laura.Madonna@xxxxxxxxx] > > (I have cut down the xsl and xml). > The problem is, I don't think the template to match on "page" is working: > > Hi, No, actually, I believe your problem is the variable definitions. They return you nodes from the whole document, instead of just the context node... It's ok (even obligatory) to have the *key* definitions as children of the xsl:stylesheet, but you probably want to move the variable declarations into the matching template, and while you're at it, go for compound keys, like <xsl:key name="row-plan_desc-page" match="row" use="concat(ancestor::page/@name, ' ', plan_desc)" /> <xsl:key name="row-heading-page" match="row" use="concat(ancestor::page/@name, ' ', heading)" /> In combination with: <xsl:template match="/form/page/pcp"> <xsl:variable name="vBenePlan" select="row[generate-id() = generate-id( key('row-plan_desc-page', concat( ancestor::page/@name,' ',plan_desc)))]" /> <xsl:variable name="vHeading" select="row[generate-id() = generate-id( key('row-heading-page', concat( ancestor::page/@name,' ',heading)))]" /> Hope this helps! Cheers, Andreas
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
RE: [xsl] FW: key, generate-id, ign, Laura Madonna | Thread | RE: [xsl] FW: key, generate-id, ign, Laura Madonna |
[xsl] Counting items related by a l, Joe K | Date | RE: [xsl] Counting items related by, M. David Peterson |
Month |