Subject: [xsl] CONTEST: How to implement "templates"? ;-) From: Aaron Optimizer Digulla <digulla@xxxxxxxx> Date: Sun, 8 Sep 2002 20:33:26 +0200 |
Hello, Here is a contest for those with too much time (you can only win the honor to be the one who came up with the best solution ;-): How can one implement "templates" with XSLT? Explanation: I have a XML file data.xml which contains lots of useful info: <root> <info id="info1"> <title>This is the title of info1</title> ... </info> <info id="info2"> ... </info> ... </root> Then I have a file layout.xml which descibes the layout of the result: <layout> <html> <head> <title><put-title-here/></title> </head> <body> ... </body> </html> </layout> And now the task: Write an XSLT script which creates one HTML page for every /root/info element in data.xml and uses the layout from layout.xml. Or to put it a different way: How can I control how the XSLT processor processes one file from another XML file? And the advanced task: Write an XSLT script which can merge different layout files. Example layout2.xml: <layout base="layout.xml"> <html> <body bgcolor="black" text="white"> </body> </html> This should behave as if all attributes in layout2.xml overwrite the attributes in layout.xml and should also work with more complex element trees. And if that's not enough for you: Extend this with a way to define xml trees in the layout and/or the source file which get expanded while the output is written. Example: <define name="h1"> <table bgcolor="blue"> <tr><td><contents/></td><tr> </table> </define> ... <use name="h1">This is put where the contents element is</use> ... Note that it should be possible to use use-elements inside of define and use elements. The winner is the person who comes up with the *fastest* *and* the *most simple* way to do this. *Fastest* means that if you need to run the XSTL processor five times to achieve this, you can't win. Ideally, everything should happen in one go or maybe two. *Most obvious* means that the XSLT stylesheet should be simple to understand (so it doesn't break that easily, it can be used as a good example in the FAQ and people don't have to study if for three days before admitting that they can't figure out what it does). This also means that the script should be generous with complicated layout files. Extension elements as described by www.exslt.org can be used. Anyone? Here are some ideas for a solution: <!-- the info elements drive this --> <xsl:template match="info"> <!-- Read and process the layout. Use a mode so we can define a generale rule for all nodes to preserve the context of the info element. --> <xsl:variable name="template"> <xsl:apply-templates select="document('layout.xml')/layout/node()" mode="tpl"> <xsl:with-param name="info" select="$info"/> </xsl:apply-templates> </xsl:variable> <xsl:message>Writing info <xsl:value-of select="$path"/>...</xsl:message> <exslt:document href="{$path}"> <xsl:apply-templates select="exslt:node-set($template)"/> </exslt:document> </xsl:template> This reads the layout and it allows to access the information in the info element which started the whole thing. But how to merge different layouts? Here is a solution for the define/use thingy: <!-- to make my life a little bit more simple, I've put define and use in the x namespace... --> <xsl:template match="x:define"> <!-- do nothin. This skips the define nodes because they should only appear in the output when they are "used" --> </xsl:template> <xsl:template match="x:use" mode="tpl"> <xsl:param name="info"/> <!-- Use what? --> <xsl:variable name="name" select="@name"/> <!-- Look up the define. How can I do that in several contexts at once? --> <xsl:variable name="template" select="//x:define[@name = $name]/node()"/> <!-- This is what should be put in place of the content element --> <xsl:variable name="content" select="node()"/> <!-- Same trick as above but we must pass the content along, too, now. Is there a better way to do this? --> <xsl:if test="$template"> <xsl:apply-templates select="$template" mode="x-tpl"> <xsl:with-param name="info" select="$info"/> <xsl:with-param name="content" select="$content"/> </xsl:apply-templates> </xsl:if> </xsl:template> <!-- Copy everything and preserve the parameters. This breaks when a pattern with mode="tpl" matches. How can this be prevented? --> <xsl:template match="@* | node()" mode="x-tpl"> <xsl:param name="info"/> <xsl:param name="content"/> <xsl:copy> <xsl:apply-templates select="@* | node()" mode="x-tpl"> <xsl:with-param name="info" select="$info"/> <xsl:with-param name="content" select="$content"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <!-- Put the contents in here --> <xsl:template match="x:contents" mode="x-tpl"> <xsl:param name="info"/> <xsl:param name="content"/> <xsl:apply-templates select="$content" mode="x-tpl"> <xsl:with-param name="info" select="$info"/> <xsl:with-param name="content" select="$content"/> </xsl:apply-templates> </xsl:template> Is there a better way to do this? Is there a way to nest these "macros"? How can one lookup macros in different contexts/document? How do create a reliable shadowing-order which won't surprise the user? And lastly how can one nest layouts as described above? Or isn't that possible? -- ============================================== Sowatec AG, CH-8330 Pfäffikon (ZH) Witzbergstr. 7, http://www.sowatec.com Tel: +41-(0)1-952 55 55 Fax: +41-(0)1-952 55 66 ---------------------------------------------- Aaron "Optimizer" Digulla, digulla@xxxxxxxxxxx ============================================== XSL-List info and archive: http://www.mulberrytech.com/xsl/xsl-list
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
Re: [xsl] Need feedback on XSL/XML , Aaron Optimizer Digu | Thread | Re: [xsl] CONTEST: How to implement, J.Pietschmann |
Re: [xsl] Can't get id() and key() , Aaron Optimizer Digu | Date | Re: [xsl] CONTEST: How to implement, J.Pietschmann |
Month |