You want to package XML data, as well as a stylesheet for converting it to HTML, into a single file.
This recipe assumes you have a browser that supports client-side XSLT transformations (IE 6.0, IE 5.x + MSXML 3.0, Netscape Navigator 6.0, etc.):
<?xml version="1.0" encoding="UTF-8"?> <?xml-stylesheet type="application/xml" href="selfcontained.xsl"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:pf="http://www.ora.com/XSLTCookbook/namespaces/portfolio"> <portfolio xmlns="http://www.ora.com/XSLTCookbook/namespaces/portfolio"> <investment> <symbol>IBM</symbol> <current>72.70</current> <paid>65.00</paid> <qty>1000</qty> </investment> <investment> <symbol>JMAR</symbol> <current>1.90</current> <paid>5.10</paid> <qty>5000</qty> </investment> <investment> <symbol>DELL</symbol> <current>24.50</current> <paid>18.00</paid> <qty>100000</qty> </investment> <investment> <symbol>P</symbol> <current>57.33</current> <paid>63</paid> <qty>100</qty> </investment> </portfolio> <xsl:output method="html" /> <xsl:attribute-set name="gain-loss-font"> <xsl:attribute name="color"> <xsl:choose> <xsl:when test="(pf:current - pf:paid) * pf:qty >= 0">black</xsl:when> <xsl:otherwise>red</xsl:otherwise> </xsl:choose> </xsl:attribute> </xsl:attribute-set> <xsl:template match="xsl:stylesheet"> <xsl:apply-templates select="pf:portfolio"/> </xsl:template> <xsl:template match="pf:portfolio"> <html> <head> <title>My Portfolio</title> </head> <body bgcolor="#FFFFFF" text="#000000"> <h1>Portfolio</h1> <table border="1" cellpadding="2"> <tbody> <tr> <th>Symbol</th> <th>Current</th> <th>Paid</th> <th>Qty</th> <th>Gain/Loss</th> </tr> <xsl:apply-templates/> </tbody> </table> </body> </html> </xsl:template> <xsl:template match="pf:investment"> <tr> <td><xsl:value-of select="pf:symbol"/></td> <td><xsl:value-of select="pf:current"/></td> <td><xsl:value-of select="pf:paid"/></td> <td><xsl:value-of select="pf:qty"/></td> <td><font xsl:use-attribute-sets="gain-loss-font"><xsl:value-of select="format- number((pf:current - pf:paid) * pf:qty, '#,##0.00')"/></font></td> </tr> </xsl:template> </xsl:stylesheet>
Two elements in this stylesheet make it work.
The first is the xml-stylesheet
processing
instruction,
which tells the browser
that the stylesheet associated with the document it loads is the very
same document. You can refer to the same document as its stylesheet
with href=""
rather than specifying the name of
the file, which is helpful if you ever rename it.
The second is the template that matches the
xsl:stylesheet
element and redirects stylesheet
processing to the embedded XML data. In this case, the elements are
in the http://www.ora.com/XSLTCookbook/namespaces/portfolio
namespace.
This recipe is somewhat of a trick to impress your friends. Intermixing content and styling, in some ways, goes against the spirit of the technology. However, delivering just a single file can be convenient, so you should not feel guilty about using this recipe if it suits your needs.
The official way to achieve these results is to embed the stylesheet in the document rather than vice versa. See http://www.w3.org/TR/xslt#section-Embedding-Stylesheets for more details. However, IE does not yet support embedded stylesheets, so this trick gets around the problem.
You can deliver content in this form without necessarily developing
the content directly in this form. The following stylesheet merges a
stylesheet and an XML file into the self-contained format. The only
two criteria are that the XML must be in a namespace and the
stylesheet should not begin processing at the root node
(/
):
<!-- generate-selfcontained.xslt --> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xso="dummy"> <!-- Reuse the identity transform --> <xsl:import href="../util/copy.xslt"/> <!-- This stylesheet will be generating styleshhet content so use xso as alias for xsl --> <xsl:namespace-alias stylesheet-prefix="xso" result-prefix="xsl"/> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <xsl:strip-space elements="*"/> <!--Not a good idea to strip space from text nodes --> <xsl:preserve-space elements="xsl:text"/> <!--The name of the file containing xml data --> <xsl:param name="datafile"/> <!-- The name of the resulting output file --> <xsl:param name="outfile"/> <xsl:template match="/"> <!-- Insert the processing instruction to tell the browser that $outfile is the stylesheet --> <xsl:processing-instruction name="xml-stylesheet"> <xsl:text>type="application/xml" href="</xsl:text> <xsl:value-of select="$outfile"/>"<xsl:text/> </xsl:processing-instruction> <xsl:apply-templates/> </xsl:template> <xsl:template match="xsl:stylesheet"> <xsl:copy> <xsl:copy-of select="@*"/> <xsl:apply-templates/> <!-- Generate the xslt that tells the <xso:template match="xsl:stylesheet"> <xso:apply-templates select="{name(document($datafile)/*)}"/> </xso:template> <!-- Insert the data --> <xsl:copy-of select="document($datafile)"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
You can use this stylesheet to transform another stylesheet and its
data into a self-contained HTML transformation. The source should be
the stylesheet, and the $datafile
is provided as a
parameter. You need an additional parameter,
$outfile
, to allow correct generation of the
xml-stylesheet
processing instruction.
Using Saxon, the generation might be invoked as:
saxon -o self-contained.xsl pf-portfolio.xslt generate-selfcontained.xslt datafile="pf-portfolio.xml" outfile="self-contained.xsl"
Where self-contained.xsl
is the name of the
resulting stylesheet and pf-portfolio.xslt
is
the stylesheet being merged with
pf-portfolio.xml
.