In my practice, XSLT is most often used as a template engine. I will not talk about why this is happening - the benefits of this technology are written enough. But even more is written about its shortcomings. It is believed that XSLT is too verbose and heavy to read, and also not the most productive. In this article I will try to collect some tips on improving the quality of XSLT code in terms of readability and expressiveness. Some of them will also allow XSLT to work somewhat faster.
Named Patterns
Many XSLT “problems” are related to the fact that we too often try to write on it in a procedural style. We are constantly trying to make Smarty out of it, but we run into one simple fact - XSLT is a declarative language, no matter how unusual it would look to us.
For example, we are trying to use named templates, perceiving them as procedures that output data in a specific format:
< xsl:template name ="CreateItemLink" >
< xsl:param name ="item" />
< a href ="/item/?id={$item/id}" >
< xsl:value-of select ="$item/name" />
</ a >< br />
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
Probably, many programmers wrote their first template this way. And he does his job well. Declarative XSLT offers a slightly different approach:
')
< xsl:template match ="item" >
< a href ="/item/?id={id}" >
< xsl:value-of select ="name" />
</ a >< br />
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
The difference is not great at all. A matter of taste and programming style. Let's see how the template will be used in the future.
Xsl: for-each and xsl: apply-templates
We would use the “imperative” version of our template like this:
< xsl:template match ="/" >
< h1 > My market </ h1 >
< xsl:for-each select ="/root/market/item" >
< xsl:call-template name ="CreateItemLink" >
< xsl:with-param name ="item" select ="." />
</ xsl:call-template >
</ xsl:for-each >
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
A "declarative" - ​​so:
< xsl:template match ="/" >
< h1 > My market </ h1 >
< xsl:apply-templates select ="/root/market/item" />
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
Now the difference has become much more noticeable:
- No more “piling up tags” for which XSLT is so famous.
- There is no longer an xsl: for-each loop — an XSLT processor does it for us that can do significant optimization.
- Our code has greatly decreased in size - not critical, but nice.
Xsl: Choose
Xsl: choose is probably one of the most verbose constructs in XSLT. Very often it is used like this:
< xsl:template match ="product" >
...
< xsl:choose >
< xsl:when test ="currencyCode = 'eur'" >
< xsl:value-of select ="'Euros'" />
</ xsl:when >
< xsl:when test ="currencyCode = 'usd'" >
< xsl:value-of select ="'Dollars'" />
</ xsl:when >
< xsl:when test ="currencyCode = 'cad'" >
< xsl:value-of select ="'Canadian dollars'" />
</ xsl:when >
...
</ xsl:choose >
...
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
There are cases when it is necessary to use it. But in this example, it is much more correct to use an external document with a dictionary:
<? xml version ="1.0" ? >
< currencies >
< currency >
< code > eur </ code >
< name > Euros </ name >
</ currency >
< currency >
< code > usd </ code >
< name > Dollars </ name >
</ currency >
< currency >
< code > cad </ code >
< name > Canadian dollars </ name >
</ currency >
...
</ currencies >
* This source code was highlighted with Source Code Highlighter .
You can access it from a template using the document () and XPath function:
< xsl:variable name ="currencies"
select ="document('cur.xml')/currencies" />
< xsl:template match ="product" >
...
< xsl:value-of select ="$currencies/currency[code=currencyCode]/name" />
...
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
It seems to me that such code is much easier to maintain and it is ideologically more correct - the data should remain data, and not turn into code that is difficult to read.
Inline transform
Many people do not know that XSLT has several “inline” transformations that do some of the work for the programmer. These transformations are done at the level of the XSLT processor, including in some implementations can work much more productively than "manual" ones.
For example, due to the built-in transformations, the template is recursively applied to the descendants of the current node. This is equivalent to this pattern:
< xsl:template match ="*|/" >
< xsl:apply-templates />
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
Moreover, if mode is used in apply-templates, then the child nodes will also be transformed with this mode. Another example is the automatic display of text nodes and attributes:
< xsl:template match ="text()|@*" >
< xsl:value-of select ="." />
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
This means that instead
< xsl:template match ="name" >
< name >< xsl:value-of select ="." /></ name >
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
can be used
< xsl:template match ="name" >
< name >< xsl:apply-templates /></ name >
</ xsl:template >
* This source code was highlighted with Source Code Highlighter .
This will (probably) work faster and make our templates more flexible and expandable.
Section in the standard .
Concat () function
Most of all, when reading templates, this code annoys me:
< xsl:value-of select ="$string1" />
< xsl:text > - </ xsl:text >
< xsl:value-of select ="$string2" />
< xsl:text > ... </ xsl:text >
* This source code was highlighted with Source Code Highlighter .
In such cases, it is much more convenient to use one <xsl: value-of />, and all the other formatting to produce using concat ().
< xsl:value-of select ="concat($string1, ' - ', $string2, '...')" />
* This source code was highlighted with Source Code Highlighter .
In general, XPath has many built-in functions that can be used to simplify code. Good documentation with examples can be found
here . Especially it is worth paying attention to substring (), translate (), etc. These functions should not be implemented in PHP or another external language - the XSLT processor will still do it better.
We use xsl: element to destination
Xsl: element is needed only in cases where the element name is dynamically determined.
< xsl:variable name ="s" > hello </ xsl:variable >
< xsl:element name ="{$s}" >
< xsl:attribute name ="site" > habr.ru </ xsl:attribute >
< xsl:value-of select ="{concat(' ', $username)}" />
</ xsl:element >
* This source code was highlighted with Source Code Highlighter .
In all other cases, you can write the tags themselves immediately. Otherwise, the code becomes too verbose without any purpose.
< hello site ="http://habr.ru/" >
< xsl:value-of select ="{concat(' ', $username)}" />
</ hello >
* This source code was highlighted with Source Code Highlighter .
I still collect such advice, t.ch. If you have something to add, be sure to write in the comments.