There are 3 method option in xsl:output element, xml, html, text. only xml option will output xml declaration. only text will disables output escaping. In your style sheet, you want to export < , text will output "<", but xml and html will output <
Tuesday, December 23, 2008
Monday, December 22, 2008
XSLT note 6 - fast search node
We can make search much faster if we generate index. We generate index by using xsl:key.
<?xml version='1.0'?>
<?xml-stylesheet type="text/xsl" href="key_sample.xsl" ?>
<titles>
<book title="XML Today" author="David Perry"/>
<book title="XML and Microsoft" author="David Perry"/>
<book title="XML Productivity" author="Jim Kim"/>
</titles>
<?xml version='1.0'?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<!-- create index for book based on @author -->
<xsl:key name="title-search" match="book" use="@author"/>
<xsl:template match="/">
<HTML>
<BODY>
<!-- use index title-search to search book where index value = 'David Perry' -->
<xsl:for-each select="key('title-search', 'David Perry')">
<div>
<xsl:value-of select="@title"/>
</div>
</xsl:for-each>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>
XSLT note 5 - Cross tab transformation
<!-- input file -->
<?xml version="1.0" encoding="utf-8" ?>
<?xml-stylesheet type="text/xsl" href="crosstab.xslt" version="1.0"?>
<ActivityList>
<Item>
<Day>1</Day>
<Type>Meal</Type>
<Name>Breakfast</Name>
<Note></Note>
</Item>
<Item>
<Day>1</Day>
<Type>Meal</Type>
<Name>Dinner</Name>
<Note>lots of stuff</Note>
</Item>
<Item>
<Day>1</Day>
<Type>Tour</Type>
<Name>Great wall</Name>
<Note></Note>
</Item>
<Item>
<Day>2</Day>
<Type>Meal</Type>
<Name>Breakfast</Name>
<Note></Note>
</Item>
<Item>
<Day>2</Day>
<Type>Airport</Type>
<Name>Transfer to hotel</Name>
<Note></Note>
</Item>
<Item>
<Day>3</Day>
<Type>Airport</Type>
<Name>Transfer to airport</Name>
<Note></Note>
</Item>
<Item>
<Day>3</Day>
<Type>Meeting</Type>
<Name>Face to face meeting</Name>
<Note></Note>
</Item>
</ActivityList>
<!-- xslt -->
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="distinctDayList" select="/ActivityList/Item[not(Day=preceding-sibling::Item/Day)]/Day"></xsl:variable>
<xsl:variable name ="distinctTypeList" select="/ActivityList/Item[not(Type=preceding-sibling::Item/Type)]/Type"></xsl:variable>
<html>
<header>
<style type="text/css">
table, td, th
{
border:solid 1px black
}
table
{
border-collapse:collapse;
}
</style>
</header>
<body>
<h1>Cross tab demo</h1>
<table>
<tr>
<!--build day row-->
<th>Type</th>
<xsl:for-each select="$distinctDayList">
<xsl:sort select="Day"/>
<th>
<xsl:value-of select="."/>
</th>
</xsl:for-each>
</tr>
<xsl:for-each select="$distinctTypeList">
<xsl:sort select="Type" data-type="text"/>
<tr>
<td>
<xsl:value-of select="."/>
</td>
<xsl:variable name="currentType" select="."></xsl:variable>
<xsl:for-each select="$distinctDayList" >
<td>
<xsl:for-each select="/ActivityList/Item[Day=current() and Type=$currentType]">
<xsl:value-of select="Name"/>
<xsl:if test="position()!=last()">
,
</xsl:if>
</xsl:for-each>
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
<!-- output file -->
<html><header><style type="text/css">
table, td, th
{
border:solid 1px black
}
table
{
border-collapse:collapse;
}
</style></header><body>
<h1>Cross tab demo</h1>
<table>
<tr>
<th>Type</th>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
<tr>
<td>Meal</td>
<td>Breakfast
,
Dinner</td>
<td>Breakfast</td>
<td></td>
</tr>
<tr>
<td>Tour</td>
<td>Great wall</td>
<td></td>
<td></td>
</tr>
<tr>
<td>Airport</td>
<td></td>
<td>Transfer to hotel</td>
<td>Transfer to airport</td>
</tr>
<tr>
<td>Meeting</td>
<td></td>
<td></td>
<td>Face to face meeting</td>
</tr>
</table>
</body>
</html>
XSLT note 3 - attribute tricks
When you want to output the value of node to destination, you can use <xsl:value-of select="xpath" />. This works only when the destination is not in side of an attribute. If the output is in side of an attribute, you need to use {}. For example.
<xsl:output method="html"/>
<xsl:variable name="sortorder">ascending</xsl:variable>
<xsl:template match="employees">
<xsl:apply-templates select="employee">
<xsl:sort select="." data-type="text" order="{$sortorder}"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="employee">
<a href="{.}"><xsl:value-of select="."/></a>
</xsl:template>
If you want to output a attribute of source element, you need to use @attributeName, or attribute::attributeName.
To add an attribute to an output node, you can also use xsl:attribute, like the following.
<xsl:template match="/">
<a>
<xsl:attribute name="href">http://google.com</xsl:attribute>
<xsl:text>Google</xsl:text>
</a>
</xsl:template>
Some time we want to convert element to attribute. For example, we want to convert
<Employee>
<Name>Fred</Name>
<Age>18</Age>
</Employee>
<Employee Name="Fred" Age="18" />
We can use this template
<xsl:template match="Employee">
<xsl:element name="{name(.)}">
<xsl:for-each select="*">
<xsl:attribute name="{name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</xsl:element>
</xsl:template>
or
<xsl:template match="Employee">
<xsl:copy>
<xsl:for-each select="*">
<xsl:attribute name="{name(.)}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</xsl:copy>
</xsl:template>
Friday, December 19, 2008
XSLT note 3 - variable
You can basically build a variable just as building any output. The simple way is
<xsl:variable name="v" select="xpath-expression" />
But you don't have to use select. Yiou can construct your variable in the constructor. Following is some sample
<xsl:variable name="Subtotals">
<!-- consturctor: can be any markup -->
<xsl:for-each select="Row">
<number>
<xsl:value-of select="Quantity * Price"/>
</number>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="header">
<h1 style="color:red;">This is demo</h1>
</xsl:variable>
<xsl:variable name="tmpResult">
<xsl:apply-templates select="$orderRow" />
</xsl:variable>
How to use variable
If a variable contains a set of nodes. you can use the variable in other XPath expressions without any limitations. For example,
<xsl:variable name="books" select="//book"/>
<xsl:for-each select="$books/author">
</xsl:for-each>
<!-- same as
<xsl:for-each select="//book/author">
</xsl:for-each>
-->
If you are using constructor to build a constructor, the variable can hold any arbitrary content, this content is called result tree fragment. You can imagine a result tree fragment (RTF) as a fragment or a chunk of XML code. You can assign a result tree fragment to a variable directly, or result tree fragment can arise from applying templates or other XSLT instructions. The following code assigns a simple fragment of XML to the variable $author.
<xsl:variable name="author">
<firstname>Jirka</firstname>
<surname>Kosek</surname>
<email>jirka@kosek.cz</email>
</xsl:variable>
Now let's say we want to extract the e-mail address from the $author variable. The most obvious way is to use an expression such as $author/email. But this will fail, as you can't apply XPath navigation to a variable of the type "result tree fragment."
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
extension-element-prefixes="exsl"
version="1.0">
...
<!-- Now we can convert result tree fragment back to node-set -->
<xsl:value-of select="msxsl:node-set($author)/email"/>
...
</xsl:stylesheet>
If you don't reuse the content of result tree, you can put copy-of statement to where you want your output to be.
<xsl:copy-of select="$header"/>
variable has a scope, like the following example shows, the $castList is referenced outside of the definition scope, so it is illegal.
<xsl:template match="Program" mode="Details">
<p>
<xsl:variable name="castList" select="CastList" />
<xsl:apply-templates select="$castList" mode="DisplayToggle" />
</p>
<xsl:apply-templates select="$castList" />
</xsl:template>
XSLT note 2 - copy template
Here is a template that copy original xml file.
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
XSLT note 1 - built-in template
A simple xslt is a empty xslt.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
</xsl:stylesheet>
But it does something, it actually is equivalent to the following style sheet. Those template are built-in and can not be removed.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="* | /">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="text() | @*">
<xsl:value-of select="." />
</xsl:template>
<xsl:template match="processing-instruction() | comment()" />
</xsl:stylesheet>
Let's take a look what is the function of the template. The first template <xsl:template match="* | /"> apply all the nodes match "* | /" with this template. * is any node, "/" is root node. The <xsl:apply-templates> element first selects a set of nodes using the expression specified in the select attribute. If this attribute is left unspecified, which here is this case, all children of the current node are selected. For each of the selected nodes, <xsl:apply-templates> directs the XSLT processor to find an appropriate <xsl:template> to apply. Templates are tested for applicability by comparing the node to the XPath expression specified in the template's match attribute. If more than one template satisfies the match pattern, the one appearing with the highest priority is chosen. If several templates have the same priority, the last in the style sheet is chosen.
It seems that <xsl:apply-templates /> is same as <xsl:apply-templates select="*" />, but it is not. It is same as <xsl:apply-templates select="* | text() | processing-instruction() | comment() "/>. If we want attribute is included we should use <xsl:apply-templates select="* | @* | text() | processing-instruction() | comment() "/>. This template basically output all the value of element nodes.