What are the principles and patterns that go into writing effective XSLT?
When I say "effective" I mean that it is
In short, I'm looking for the best practices for XSLT.
I've already seen the question regarding efficiency, but efficient code loses its value if you can't understand what it's doing.
XSLT is very widely used. As far as we can judge from metrics like the number of StackOverflow questions, it is in the top 30 programming languages, which probably makes it the top data-model-specific programming language after SQL. But XSLT isn't widely used client-side, that is, in the browser.
XSLT is difficult to work with, but once you conquer it you will have a very thorough understanding of the DOM and schema. If you also XPath, then you on your way to learning functional programming and this will expose to new techniques and ways about solving problems.
XSL stands for EXtensible Stylesheet Language. It is a styling language for XML just like CSS is a styling language for HTML. XSLT stands for XSL Transformation. It is used to transform XML documents into other formats (like transforming XML into HTML).
One can often find examples of beautiful XSLT code, especially when XSLT is used as a functional programming language.
For examples see this article on FXSL 2.0 -- the Functional Programming library for XSLT 2.0.
As an FP language XSLT is also a declarative language. This, among other things means that one declares, specifies existing relationships.
Such a definition often does not need any additional code to produce a result -- it itself is its own implementation, or an executable definition or executable specification.
Here is a small example.
This XPath 2.0 expression defines the "Maximum Prime Factor of a natural number":
if(f:isPrime($pNum))
then $pNum
else
for $vEnd in xs:integer(floor(f:sqrt($pNum, 0.1E0))),
$vDiv1 in (2 to $vEnd)[$pNum mod . = 0][1],
$vDiv2 in $pNum idiv $vDiv1
return
max((f:maxPrimeFactor($vDiv1),f:maxPrimeFactor($vDiv2)))
To pronounce it in English, the maximum prime factor of a number pNum
is the number itself, if pNum
is prime, otherwise if vDiv1
and vDiv2
are two factors of pNum
, then the maximum prime factor of pNum
is the bigger of the maximum prime factors of vDiv1
and vDiv2
.
How do we use this to actually calculate the Maximum Prime Factor in XSLT? We simply wrap up the definition above in an <xsl:function>
and ... get the result!
<xsl:function name="f:maxPrimeFactor" as="xs:integer">
<xsl:param name="pNum" as="xs:integer"/>
<xsl:sequence select=
"if(f:isPrime($pNum))
then $pNum
else
for $vEnd in xs:integer(floor(f:sqrt($pNum, 0.1E0))),
$vDiv1 in (2 to $vEnd)[$pNum mod . = 0][1],
$vDiv2 in $pNum idiv $vDiv1
return
max((f:maxPrimeFactor($vDiv1),f:maxPrimeFactor($vDiv2)))
"/>
</xsl:function>
We can, then, calculate the MPF for any natural number, for example:
f:maxPrimeFactor(600851475143)
= 6857
As for efficiency, well, this transformation takes just 0.109 sec.
Other examples of both ellegant and efficient XSLT code:
Here are some rules for writing "quality XSLT code", as taken from Mukul Ghandi's blog.
They can be checked/enforced using a tool developed by Mukul:
DontUseDoubleSlashOperatorNearRoot: Avoid using the operator // near the root of a large tree.
DontUseDoubleSlashOperator: Avoid using the operator // in XPath expressions.
SettingValueOfVariableIncorrectly: Assign value to a variable using the 'select' syntax if assigning a string value.
EmptyContentInInstructions: Don't use empty content for instructions like 'xsl:for-each' 'xsl:if' 'xsl:when' etc.
DontUseNodeSetExtension: Don't use node-set extension function if using XSLT 2.0.
RedundantNamespaceDeclarations: There are redundant namespace declarations in the xsl:stylesheet element.
UnusedFunction: Stylesheet functions are unused.
UnusedNamedTemplate: Named templates in stylesheet are unused.
UnusedVariable: Variable is unused in the stylesheet.
UnusedFunctionTemplateParameter: Function or template parameter is unused in the function/template body.
TooManySmallTemplates: Too many low granular templates in the stylesheet (10 or more).
MonolithicDesign: Using a single template/function in the stylesheet. You can modularize the code.
OutputMethodXml: Using the output method 'xml' when generating HTML code.
NotUsingSchemaTypes: The stylesheet is not using any of the built-in Schema types (xs:string etc.), when working in XSLT 2.0 mode.
UsingNameOrLocalNameFunction: Using name() function when local-name() could be appropriate (and vice-versa).
FunctionTemplateComplexity: The function or template's size/complexity is high. There is need for refactoring the code.
NullOutputFromStylesheet: The stylesheet is not generating any useful output. Please relook at the stylesheet logic.
UsingNamespaceAxis: Using the deprecated namespace axis, when working in XSLT 2.0 mode.
CanUseAbbreviatedAxisSpecifier: Using the lengthy axis specifiers like child::, attribute:: or parent::node().
UsingDisableOutputEscaping: Have set the disable-output-escaping attribute to 'yes'. Please relook at the stylesheet logic.
NotCreatingElementCorrectly: Creating an element node using the xsl:element instruction when could have been possible directly.
AreYouConfusingVariableAndNode: You might be confusing a variable reference with a node reference. (contributed by, Alain Benedetti)
IncorrectUseOfBooleanConstants: Incorrectly using the boolean constants as 'true' or 'false'. (contributed by, Tony Lavinio)
ShortNames: Using a single character name for variable/function/template. Use meaningful names for these features.
NameStartsWithNumeric: The variable/function/template name starts with a numeric character
Best practice 1 : use templates in stead of < xsl:for-each > whenever you can (which is 99% of the cases)
(may I add MAINTAINABILITY as extra ingredient in the best practices, imho even the most important one)
For understanding xsl you realy need a bit of practice.
Not understanding what sth. is doing is very relative of course.
That goes doube for XSLT, since the xsl:for-each construct tends to be
for a novice, but is in fact
than templates, and only
NEVER, EVER USE THE < xsl:for-each > ELEMENT!
I admit, the title is somewhat exaggerated, there do exist, I've been told, cases in which a "xsl for each" can have it's merits, but those cases are very, very rare.
I once had to come up with a fairly complicated xml/xslt client site in less than a week, and used the for-each element all over the place. Now, several years later and, sort of, wiser, I took my time and rewrote the initial code, using only templates. The code now is much much cleaner and more adaptable.
Either you know this, or either you should : < xsl:template > and < xsl: apply-templates> are almost always the way to go. If you are xsl-ing, and you don't fully understand these tags, stop your work now, learn them, get a aha-erlebnis, and continue your work a as a reborn (wo)man.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With