This post discusses namespaces in XML documents and XSLT 1.0 stylesheets. In particular, it demonstrates a number of techniques to control namespace prefixes and declarations in outputted documents.
|
|
||
|
This post discusses namespaces in XML documents and XSLT 1.0 stylesheets. In particular, it demonstrates a number of techniques to control namespace prefixes and declarations in outputted documents. I recently answered a question on a popular programmers forum about how to store and access an array of user-defined variables in a stylesheet and then loop though those variables. I realized that many developers are not familar with the available techniques for doing this and decided to add an entry in my blog about this topic. User-defined variable arrays within stylesheets are not part of the XSLT specification. The usual way to handle this problem in XSLT 1.0 stylesheets is to define a user-defined top-level element which belongs to a non-null namespace which is different from the XSLT namspace. These user-defined top-level elements are typically used to store error messages, lookup data, etc. You can then access these user-defined elements from within your stylesheet by treating the stylesheet as an additional source document and loading it using the document() function with an empty string as the first argument. An empty string is interpreted to mean the current stylesheet. The following stylesheet demonstrates this method. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:foo="http://foo.com" exclude-result-prefixes="foo"> <xsl:output method="text" encoding="utf-8"/> <foo:vars> <foo:var name="z1">A</foo:var> <foo:var name="z2">B</foo:var> <foo:var name="z3">C</foo:var> <foo:var name="z4">D</foo:var> </foo:vars> <xsl:template match="/"> <xsl:for-each select="document(”)/xsl:stylesheet/foo:vars/foo:var" > <xsl:value-of select="." /> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if> </xsl:for-each> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet> If you are using XSLT 2.0, this method is no longer needed as simpler and more elegant methods are available to us. For example, you can store and directly access the variables using the <xsl:variable> element as shown in the following stylesheet. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" > <xsl:output method="text" encoding="utf-8"/> <xsl:variable name="z1" select="’A'" /> <xsl:variable name="z2" select="’B'" /> <xsl:variable name="z3" select="’C'" /> <xsl:variable name="z4" select="’D'" /> <xsl:variable name="vars" select="$z1, $z2, $z3, $z4" /> <xsl:template match="/"> <xsl:for-each select="$vars" > <xsl:value-of select="." /> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if> </xsl:for-each> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet> Another way to store and access this data in a XSLT 2.0 stylesheet is to use a global variable definition as shown below. <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0" > <xsl:output method="text" encoding="utf-8"/> <xsl:variable name="vars"> <var name="z1">A</var> <var name="z2">B</var> <var name="z3">C</var> <var name="z4">D</var> </xsl:variable> <xsl:template match="/"> <xsl:for-each select="$vars/var" > <xsl:value-of select="." /> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if> </xsl:for-each> <xsl:text> </xsl:text> </xsl:template> </xsl:stylesheet> All three stylesheets output the same data. You may be wondering about this image. It is from the screen of my laptop. I used xsltproc on Microsoft Vista SUA for the XSLT 1.0 transformation and Saxon 9 in Microsoft Powershell v2.0 CTP2 for the two XSLT 2.0 transformations. Note that the A recent problem that was posed to me concerned how to copy the entire contents of an XML document with certain exceptions. Turns out that the simplest way to handle this requirement in XSLT1.0 was to include the standard XSL identity template in my stylesheet and add another template to handle the exception. A simple example will make things clearer. Suppose we have the following XML document (which I shamelessly copied from W3Schools.com and modified to simplify) and we want to copy this document in its entirety except for details of CDs by a specific artist. <?xml version="1.0"?> <CATALOG> <CD> <TITLE>Empire Burlesque</TITLE> <ARTIST>Bob Dylan</ARTIST> <COUNTRY>USA</COUNTRY> <COMPANY>Columbia</COMPANY> <YEAR>1985</YEAR> </CD> <CD> <TITLE>Hide your heart</TITLE> <ARTIST>Bonnie Tyler</ARTIST> <COUNTRY>UK</COUNTRY> <COMPANY>CBS Records</COMPANY> <YEAR>1988</YEAR> </CD> <CD> <TITLE>Greatest Hits</TITLE> <ARTIST>Dolly Parton</ARTIST> <COUNTRY>USA</COUNTRY> <COMPANY>RCA</COMPANY> <YEAR>1982</YEAR> </CD> <CD> <TITLE>One night only</TITLE> <ARTIST>Bee Gees</ARTIST> <COUNTRY>UK</COUNTRY> <COMPANY>Polydor</COMPANY> <YEAR>1998</YEAR> </CD> <CD> <TITLE>Sylvias Mother</TITLE> <ARTIST>Dr.Hook</ARTIST> <COUNTRY>UK</COUNTRY> <COMPANY>CBS</COMPANY> <YEAR>1973</YEAR> </CD> <CD> <TITLE>Maggie May</TITLE> <ARTIST>Rod Stewart</ARTIST> <COUNTRY>UK</COUNTRY> <COMPANY>Pickwick</COMPANY> <YEAR>1990</YEAR> </CD> </CATALOG> Below is the stylesheet. It accepts one parameter, i.e. the name, or part of the name, of an artist. It copies all nodes and attributes to the output document except those nodes and attributes related to the artist whose name matches or is is a superset of the inputted string. <?xml version="1.0"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"> <xsl:param name="artist"></xsl:param> <xsl:output method="xml"/> <xsl:template match="node() | @*"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> <xsl:template match="/CATALOG/CD"> <xsl:if test="not(contains(./ARTIST,$artist))"> <xsl:copy-of select="." /> </xsl:if> </xsl:template> </xsl:stylesheet> The first template is the standard identity template from the XSL 1.0 Transformations Recommendation. It matches all attributes and all nodes being children of other nodes, and copies them to the output document. The second template cancels the automatic copying of the <CD> nodes and children, checks to see that the name, or part of the name, of the artist does not match the search string, and only does a deep copy of the nodes and attributes to the output document if there is no match. Assuming you are on a Linux system, you can use the command line utility xsltproc (which is part of the XSLT C library for GNOME) to transform the input document into the following output document when "Stewart" is passed as a parameter to the stylesheet. BTW, note the use of Java style string quotes for the parameter string. $ xsltproc -param artist "’Stewart’" file.xsl file.xml Here is the output document. It is a copy of the input document except the "record" for the Rod This post discusses the problem of dynamic XPath expression evaluation in XSLT 1.0 and XSLT 2.0 and how to handle it using an EXSLT extension function. Support for date and time formating in the XSLT 1.0 specification is non-existent. This did not mean that a person cannot format date and time strings using XSLT 1.0; it just makes it much harder to do so and adds many extra lines of code to stylesheets. However it is something that everybody who develops stylesheets ends up having to do. In this post I show you several ways to format dates in XSLT 1.0 and discuss some of the new dateTime formating and manipulation functions in XSLT 2.0 and XPath 2.0. |
||
|
Copyright © 2005-2012 Finnbarr P. Murphy. All Rights Reserved. |
||