While searching for some profiling tools for XSLT, I came across this post. Since a lot of people there suggested to just post the code and offered to give feedback on that, I was wondering if anyone could give me some feedback on mine. I tried this (http://www.saxonica.com/documentation/#!using-xsl/performanceanalysis), but the output html is not very detailed.
I'm new to XSLT and usually work with python/perl, where regex support is much better (however, I won't rule out the possibility that it's just my very basic understanding of XSLT). For the purpose of this project however, I had to work with XSLT. It could be that I'm forcing it to do things in a very unnatural way. Any comments -on performance in particular, but anything else is also welcome, as I'd like to learn- are welcome!
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2005/xpath-functions">
<xsl:template name="my_terms">
<xsl:variable name="excludes" select="not (codeblock or draft-comment or filepath or shortdesc or uicontrol or varname)"/>
<!-- leftover example of how to work with excludes var -->
<!--<xsl:if test=".//*[$excludes]/text()[contains(.,'access management console')]"><li class="prodterm"><b>PB QA:access management console should be "AppCenter"</b></li></xsl:if>-->
<!-- Loop through all sentences and check for deprecated stuff -->
<xsl:for-each select=".//*[$excludes]/text()">
<xsl:variable name="sentenceList" select="tokenize(., '[\.!\?:;]\s+')"/>
<xsl:variable name="segment" select="."/>
<!-- main sentence loop -->
<xsl:for-each select="$sentenceList">
<xsl:variable name="sentence" select="."/>
<!-- very rudimentary sentence length check -->
<xsl:if test="count(tokenize(., '\W+')) > 30"> <li class="prodterm"><b>Sentence too long:</b> <xsl:value-of select="."/></li></xsl:if>
<!-- efforts to flag the shady case of the gerund -->
<xsl:if test="matches(., '\w+ \w+ing (the|a)')">
<!-- some extra checks to weed out the false positives -->
<xsl:if test="not(matches(., '\b(on|about|for|before|while|when|after|by|a|the|an|some|all|every) \w+ing (the|a)', '!i')) and not(matches(., 'during'))">
<li class="prodterm"><b>Possible unclear usage of gerund. If so, consider rewriting:</b> <xsl:value-of select="."/></li>
</xsl:if>
</xsl:if>
<!-- comma's after certain starting phrases -->
<xsl:if test="matches(., '^\s*Therefore[^,]')"><li class="prodterm"><b>Use a comma after starting a sentence with 'Therefore':</b> <xsl:value-of select="."/></li></xsl:if>
<xsl:if test="matches(., '^\s*(If you|Before|When)[^,]+$')"><li class="prodterm"><b>Use a comma after starting a sentence with 'Before', 'If you' or 'When':</b> <xsl:value-of select="."/></li></xsl:if>
<!-- experimenting with phrasal verbs (if there are a lot of verbs in phrasalVerbs.xml, it will be better to have this as the main loop (and do it outside the sentence loop)) -->
<xsl:for-each select="document('phrasalVerbs.xml')/verbs/verb[matches($sentence, concat('.* ', ./#text, ' .*'))]">
<xsl:variable name="verbPart" select="."/>
<xsl:for-each select="$verbPart/particles/particle/#text[matches($sentence, .) and not(matches($sentence, concat($verbPart/#text, ' ', .)))]">
<xsl:variable name="particle" select="."/>
<li class="prodterm"><b>Separated phrasal verb found in:</b> <xsl:value-of select="$sentence"/></li>
</xsl:for-each>
</xsl:for-each>
<!-- checking if conditionals (should be followed by then) -->
<xsl:if test="matches($sentence, '^\s*If\b', '!i') and not(matches($sentence, '\bthen\b', '!i'))"><li class="prodterm"><b>Conditional If found, but no then:</b> <xsl:value-of select="."/></li></xsl:if>
<!-- very dodgy way of detecting passive voice -->
<!--<xsl:if test="matches($sentence, '\b(are|can be|must be) \w+ed\b', '!i')"><li class="prodterm"><b>PB QA:Possible passive voice. If so, consider using active voice for:</b> <xsl:value-of select="."/></li></xsl:if>-->
<xsl:for-each select='document("generalDeprecatedTermsAndPhrases.xml")/terms/dt'>
<xsl:variable name="pattern" select="./#pattern"/>
<xsl:variable name="message" select="./#message"/>
<xsl:variable name="regexFlag" select="./#regexFlag"/>
<!-- <xsl:if test="matches($sentence, $pattern, $regexFlag)"> -->
<xsl:if test="matches($sentence, concat('(^|\W)', $pattern, '($|\W)'), $regexFlag)"> <!-- This is the work around for not being able to use \b when variable is passed on inside matches() -->
<li class="prodterm"><b><xsl:value-of select="$message"/> in: </b> <xsl:value-of select="$sentence"/> </li>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
To get an idea, the stripped down version of my "generalDeprecatedTermsAndPhrases.xml" looks like this:
<dt pattern='to be able to' message="Use 'to' instead of 'to be able to'" regexFlag="i"></dt>
</terms>
The reason that Saxon's profile is not very detailed is that your code is so monolithic: it's all in one great big template rule.
However, being monolithic isn't by itself the cause of any performance problems.
First observation is a functionality problem: your variable
<xsl:variable name="excludes" select="not (codeblock or draft-comment or filepath or shortdesc or uicontrol or varname)"/>
doesn't do what you think. It's evaluated with the root document node as the context item, and its value is a boolean which is true if the outermost element has a name which is not one of those listed. So I think your xsl:for-each that uses [$excludes] as a predicate is applying to all elements, whereas I suspect you intended it to apply to selected elements. I don't know how much that affects the performance.
The main influence on performance will be the cost of evaluating the regular expressions. The best way to find out which ones are causing the problem is to measure the impact of removing them one-by-one. When you've isolated the problem, there may be a way of rewriting the regular expression to make it perform better (e.g. by making it avoid backtracking).
Related
I have a piece of an XSLT stylesheet that works as expected using xsltproc but produces a different output in my actual application, where the transform is applied via org.jdom.transform.XSLTransformer (jdom 1.0), I believe using Xalan.
Stylesheet snippet (this is part of a larger template that starts like this: <xsl:template match="/dspace:dim[#dspaceType='ITEM']">):
<xsl:if test="//dspace:field[#mdschema='dc' and #element='rights']">
<rightsList>
<xsl:if test="//dspace:field[#mdschema='dc' and #element='rights' and not(#qualifier) and #language='*']">
<rights>
<xsl:if test="//dspace:field[#mdschema='dc' and #element='rights' and #qualifier='uri' and #language='*']">
<xsl:attribute name="rightsUri">
<xsl:value-of select="//dspace:field[#mdschema='dc' and #element='rights' and #qualifier='uri' and #language='*']"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="//dspace:field[#mdschema='dc' and #element='rights' and not(#qualifier) and #language='*']" />
</rights>
</xsl:if>
<xsl:apply-templates select="//dspace:field[#mdschema='dc' and #element='rights' and not(#language='*')]" />
</rightsList>
</xsl:if>
and
<xsl:template match="//dspace:field[#mdschema='dc' and #element='rights' and not(#language='*')]">
<rights><xsl:value-of select="." /></rights>
</xsl:template>
XML snippet:
<dim:dim dspaceType="ITEM" xmlns:dim="http://www.dspace.org/xmlns/dspace/dim">
<dim:field element="rights" language="en_NZ" mdschema="dc">Actual text redacted</dim:field>
<dim:field element="rights" language="*" mdschema="dc">Attribution 3.0 New Zealand</dim:field>
<dim:field element="rights" qualifier="uri" language="*" mdschema="dc">http://creativecommons.org/licenses/by/3.0/nz/</dim:field>
</dim:dim>
With xsltproc, this produces
<rightsList>
<rights rightsUri="http://creativecommons.org/licenses/by/3.0/nz/">Attribution 3.0 New Zealand</rights>
<rights>Actual text redacted</rights>
</rightsList>
In my application, this produces
<rightsList>
<rights>Actual text redacted</rights>
<rights>Attribution 3.0 New Zealand</rights>
<rights>http://creativecommons.org/licenses/by/3.0/nz/</rights>
</rightsList>
So to me it looks like the not(#qualifier) bit doesn't work using jdom.
I'd appreciate any insight into what's going on here and how I might change the stylesheet to get the same result in my application that I currently get via xsltproc.
Edited to add: just in case it makes any difference, the stylesheet starts out as
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dspace="http://www.dspace.org/xmlns/dspace/dim"
xmlns:exslt="http://exslt.org/common"
xmlns="http://datacite.org/schema/kernel-3"
extension-element-prefixes="exslt"
exclude-result-prefixes="exslt"
version="1.0">
and also includes this template:
<!-- Don't copy everything by default! -->
<xsl:template match="#* | text()" />
See my answer below the XML structure is actually different from what I thought it was, so the problem wasn't in the XSL after all.
Apart from solving your original problem, let's have a quick look at how to reorganize your code.
You use a lot of //foo expressions. Starting an expression with //foo means "search the whole document, at any level, for the element with the name foo". Apart from this being a potentially expensive operation, this often has unwanted side effects and makes your code hard to read, because it requires you to specify each element uniquely, leading to a lot of duplicated code.
You also use a lot of xsl:if, but in XSLT, it is hardly ever necessary to use if-statements (an exception in XSLT 1.0 and 2.0 being when you deal with something other than nodes). In almost all cases, you can replace an xsl:if with a simple xsl:apply-templates.
That said, let's have a look how we can rewrite your code to get the same effect and have less chance for error:
<xsl:if test="//dspace:field[#mdschema='dc' and #element='rights']">
<rightsList>
.....
Is similar to having a matching template as follows (assuming you have a throw-away template for uninteresting nodes):
<xsl:template match="dspace:dim[dspace:field[#mdschema='dc' and #element='rights']]">
<rightsList>
This says: if you encounter a dim element with any field element that has those properties set, then output <rightsList>.
Then you have:
<xsl:if test="//dspace:field[#mdschema='dc' and #element='rights' and not(#qualifier) and #language='*']">
<rights>
Which is precisely equivalent to the following apply-template expression (assuming a matching template with it):
<xsl:apply-templates select="dspace:field[#mdschema='dc' and #element='rights' and not(#qualifier) and #language='*']" />
Here we find that a little bit below that, we have an almost equivalent expression, this time with not(#language='*'). So let's see if we can get rid of those duplicate expressions altogether.
First, let's go back a bit and have a look at what you were doing:
If anywhere any "dc" and "rights", then create a <rightsList>
If anywhere any of these have do not have a qualifier but have a language "*", create <rights>
Inside this, create an attribute rightsUri if anywhere any qualifier has value "uri" and language "*", set its value to the first such you find
After this <rights> element (there can be at most one of them in your current structure), create a list of <rights> for each field element with language "*"
If this is correct, then this can be rewritten as follows:
<xsl:template match="dspace:dim[dspace:field[#mdschema='dc' and #element='rights']]">
<xsl:variable name="adjusted">
<xsl:copy-of select="dspace:field[#mdschema='dc' and #element='rights']"/>
</xsl:variable>
<rightsList>
<xsl:apply-templates select="exsl:node-set($adjusted)/*[not(#qualifier) and #language='*'][1]" mode="noquali"/>
<xsl:apply-templates select="exsl:node-set($adjusted)/*[not(#language='*')]" />
</rightsList>
</xsl:template>
<xsl:template match="dspace:field" mode="noquali">
<rights>
<xsl:apply-templates select="/dspace:field[#qualifier='uri' and #language='*'][1]" mode="uri"/>
<xsl:value-of select="."/>
</rights>
</xsl:template>
<xsl:template match="dspace:field" mode="uri">
<xsl:attribute name="rightsUri" select="." />
</xsl:template>
<!-- matching anything else -->
<xsl:template match="dspace:field">
<rights><xsl:value-of select="." /></rights>
</xsl:template>
The exsl:node-set function is supported by just about every XSLT 1.0 processor, just add the namespace xmlns:exsl="http://exslt.org/common" to your xsl:stylesheet declaration.
Note that I added a few times [1] to the select-expressions. While you don't do that in your code, your current code has the same effect, but if you use apply-templates, if you encounter multiple matches, you have to specify that you are only interested in the first match.
I think your code can be further simplified, but I wanted to make sure that the logic remains exactly the same. As you can see, the end result is without any //. However, you do see one /, which is now pointing to the root of the node-set, which conveniently only has the nodes you are interested in: the ones with schema "dc" and "rights" element attributes, so we do not have to repeat that expression over and over again.
You may try this rewrite and see if it helps with your current bug, otherwise I'll gladly to help you further.
Edit
After your edit, your original context item will have been dspace:dim already. If you don't mind always outputting <rightsList> (even if it ends up empty), you can simply replace my first template match pattern above with your existing dspace:dim pattern.
Duh. Forest/trees indeed. Even though the language attribute is called "language" pretty much everywhere else in the application (see also, the XML snippet I gave), it is actually called "lang" in the XML that my stylesheet operates on - I finally gave in and used this answer to be sure what the XML structure is. Surprise!
Anyway, I followed the advice Abel gave in his answer in part and simplified the templates for this particular case quite a bit. I now just have
<xsl:if test="dspace:field[#mdschema='dc' and #element='rights']">
<rightsList>
<xsl:apply-templates select="dspace:field[#mdschema='dc' and #element='rights']"/>
</rightsList>
</xsl:if>
in the big template, plus a couple of custom ones:
<xsl:template match="dspace:field[#mdschema='dc' and #element='rights']">
<xsl:choose>
<xsl:when test="#qualifier='uri'"/>
<xsl:otherwise>
<rights>
<xsl:if test="#lang='*'">
<xsl:apply-templates select="//dspace:field[#mdschema='dc' and #element='rights' and #qualifier='uri' and #lang='*'][1]" mode="rightsURI"/>
</xsl:if>
<xsl:value-of select="."/>
</rights>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="dspace:field[#mdschema='dc' and #element='rights' and #qualifier='uri' and #lang='*']" mode="rightsURI">
<xsl:attribute name="rightsURI"><xsl:value-of select="."/></xsl:attribute>
</xsl:template>
In my Umbraco CMS site, I'm making a list of node "widgets" for content editors to use, with a list of many options they can toggle to change the display. This often involves wrapping an element with an anchor, div, or something else.
Using XSLT to display these from the XML output, I've put together what a kludge approach as I'm a very new XSLT beginner.
What I've come to as a solution is multiple nested apply-templates. This creates a large list of conditions, often asking repeat checks, which trees out pretty huge. It's a bit of a nightmare to manage.
It looks as such (but with more than two options in each choose):
<xsl:template match="/">
<xsl:choose>
<xsl:when test="type='1'">
<xsl:apply-templates select="widgetContent" mode="type_1" />
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="widgetContent" mode="type_default" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="wigetContent" mode="type_1">
<xsl:choose>
<xsl:when test="./wrap_with_hyperlink != 0">
<xsl:element name="a">
<xsl:apply-templates select="." mode="hyperlink_wrapped" />
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="widgetContent" mode="not_hyperlink_wrapped" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
What can I do to reduce this tangled mess? I've structured the conditions to be as top down as much as possible, but there are definitely repeated checks where type_2 has to ask the same questions as type_1 all over again.
(edit: clarity) Because the design is basically an onion, type_1 is wrapped by certain tags, type_2 is wrapped by different tags. Next layer in, both could be wrapped by the same tags, and so forth. What would be perfect is:
<xsl:if test="this_wrap_style = 1"><xsl:element name="abc"></xsl:if>
<xsl:if test="this_wrap_style = 2"><xsl:element name="xyz"></xsl:if>
(everything else)
</abc> //if it exist.
</xyz> //etc
Which definitely doesn't work.
Some of this has been reduced by using Umbraco Doc Types for different widget controls, but part of the nature is that to the ideal structure for content editors is selecting a box widget will give them 5 different types of widget boxes (or more) to choose from, and a coherent back end isn't as important.
Thank you all for your time.
<!--Store data in processing instruction nodes in a separate XML file-->
<?xml version='1.0' encoding="utf-8"?>
<root>
<?_1 div?>
<?_2 p?>
</root>
type_1 is wrapped by certain tags, type_2 is wrapped by different tags.
<xsl:variable name="divider" select="document('condition.xml')//processing-instruction(concat('_', $type) )" />
<xsl:variable name="equalizer" select="'span'"/>
<xsl:element name="{$divider}">
...
</xsl:element>
Next layer in, both could be wrapped by the same tags
<xsl:if test="contains('1,2',$type)">
<xsl:element name="{$equalizer}">
...
</xsl:element>
</xsl:if>
I am trying to make this (xml 1.0) code work . I am new to this and already exhausted myself in
trying different ways. Does someone know my mistake?
<xsl:for-each select="News/Sport">
<xsl:if test="local-name()='Basketball'">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:value-of select="News/Sport/Basketball/Phrases/Phrase"/>
</xsl:if>
</xsl:for-each>
When I transform it into an HTML file the content doesn't show up. When I remove the xsl:for each and the xsl:if statements the content is successfully presented. I only wish that the content is first checked (if it is available in the XML file) and if yes, that it is taken from the XML content.
Thank you in advance for your help!
EDIT:
This is my XML code
<News>
<Sport>
<Basketball>
<Phrases>
<Phrase>Zach Randolph recovered the opening tipoff in Game 1 of the Western Conference Finals, and he didn’t touch the ball again until the possession following the Grizzlies’ first timeout.
</Phrase>
<Phrases>
</Basketball>
</Sport>
</News>
EDIT2:
Could you tell me why I cannot apply a template inside this below function? Only the text works now:(
<xsl:for-each select="News/Sport[Basketball]">
<xsl:apply-templates select="News/Sport/*" />
</xsl:for-each>
<xsl:template match="Basketball">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:apply-templates select="Phrases/Phrase"/>
</xsl:template>
<xsl:for-each select="News/Sport">
<xsl:if test="local-name()='Basketball'">
In this if test, the context node is a Sport element, so local-name() will always be Sport and will never equal Basketball.
I only wish that the content is first checked (if it is available in the XML file) and if yes, that it is taken from the XML content.
The usual way to handle this sort of thing in XSLT is to define templates matching the various nodes that might be present and then applying templates to all the nodes that are actually found. If there are no nodes of a particular type then the corresponding template will not fire
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" />
<xsl:template match="/">
<html>
<body>
<!-- apply templates that match all elements inside Sport, which may
be Basketball, Football, etc. -->
<xsl:apply-templates select="News/Sport/*" />
</body>
</html>
</xsl:template>
<!-- when we find a Basketball element ... -->
<xsl:template match="Basketball">
<p>
<xsl:text>Basketball Sport</xsl:text>
</p>
<xsl:apply-templates select="Phrases/Phrase"/>
</xsl:template>
<!-- when we find a Football element ... -->
<xsl:template match="Football">
<p>
<xsl:text>Football Sport</xsl:text>
</p>
<!-- whatever you need to do for Football elements -->
</xsl:template>
<xsl:template match="Phrase">
<p><xsl:value-of select="." /></p>
</xsl:template>
</xsl:stylesheet>
That way you don't need any explicit for-each or if, the template matching logic handles it all for you.
You are missing the idea of a context node. Within a xsl:for-each, everything you refer to is about or relative to the selected nodes. So, for instance, within <xsl:for-each select="News/Sport">, the context node is the Sport elements, and a test like <xsl:if test="local-name()='Basketball'"> is always going to be false because the local name is always Sport.
It looks like you want just <xsl:if test="Basketball"> which tests whether there are any Basketball child nodes of the current Sport node.
The same thing applies to <xsl:value-of select="News/Sport/Basketball/Phrases/Phrase"/>. Because everything is relative to the Sport node, XSLT is looking for News/Sport/Basketball/Phrases/Phrase within the Sport element, which never exists.
In addition, you can just put text in literally: there is no need for an xsl:text element here, so your code should look like
<xsl:for-each select="News/Sport">
<xsl:if test="Basketball">
<p>Basketball Sport</p>
<xsl:value-of select="Basketball/Phrases/Phrase"/>
</xsl:if>
</xsl:for-each>
You can refine this further by adding a predicate to the for-each selection, so that it matches only Sport elements with a Basketball child. Like this
<xsl:for-each select="News/Sport[Basketball]">
<p>Basketball Sport</p>
<xsl:value-of select="Basketball/Phrases/Phrase"/>
</xsl:for-each>
As I have understood the match atribute of a template tag, it defines what part of the xml tree that will be enclosed in the template.
However ther seem to be some exceptions, I have a working peace of code, lite this:
<xsl:template match="/root/content">
<xsl:for-each select="/root/meta/errors/error">
<p>
<strong>Error:</strong> <xsl:value-of select="message" /> (<xsl:value-of select="data/param" />)<br />
<xsl:for-each select="data/option">
<xsl:value-of select="." /><br />
</xsl:for-each>
</p>
<br /><br />
</xsl:for-each>
</xsl:template>
But when I try to add a conditional like this:
<xsl:template match="/root/content">
<xsl:if test="not(/root/meta/error/errors/data/param)"-->
<xsl:for-each select="/root/meta/errors/error">
<p>
<strong>Error:</strong> <xsl:value-of select="message" /> (<xsl:value-of select="data/param" />)<br />
<xsl:for-each select="data/option">
<xsl:value-of select="." /><br />
</xsl:for-each>
</p>
<br /><br />
</xsl:for-each>
<xsl:call-template name="trip_form">
<xsl:with-param name="type" select="'driver'" />
<xsl:with-param name="size" select="'savetrip'" />
</xsl:call-template>
</xsl:if>
</xsl:template>
It doesn't work any more, why, and how can I make it work again?
Attribute matches are applied when you ask for it (you are pulling with complex and unneeded for-each resulting in no attribute matching at all), otherwise they are ignored. That's why the copy idiom is used with specific attribute apply-templates:
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="* | #*" />
</xsl:copy>
</xsl:template>
When it comes to the order in which they are applied, the order is the document order, which means: after the element is applied, its attributes will be applied (in undetermined order) and then the element's children are applied. Attributes never have children and their parent is the containing element.
"it defines what part of the xml tree that will be enclosed in the template."
No. It is called when the processor encounters input that matches the specification, or when you specifically apply this input by using xsl:apply-templates. Your code should not use xsl:for-each, that's rarely needed. Instead, use xsl:apply-templates. This will also give you the possibility to match the attributes when you like.
Normally, you don't (need to) specify the parent in the match-attribute of apply-templates. And you surely don't write down the whole path inside the templates each time, that will wreak havoc on usability of your stylesheet... Try something like this instead and have a look at some XSL tutorials on the net (w3schools provides some basic information and Tennison's book is next to invaluable to learn about this variant of functional programming):
<xsl:template match="/">
<xsl:apply-templates select="/root/content" />
</xsl:template>
<xsl:template match="content">
<xsl:apply-templates select="errors/error" />
</xsl:template>
<xsl:template match="error">
<p>
<strong>Error:</strong>
<xsl:value-of select="message" />
(<xsl:value-of select="data/param" />)
<br />
<xsl:apply-templates select="data/option" />
</p>
<br /><br />
</xsl:template>
<xsl:template match="option">
<xsl:value-of select="." /><br />
</xsl:template>
"It doesn't work any more, why, and how can I make it work again?"
Because your if-statement is probably always true (or always false). Reason: if anywhere in your document the XPath is correct, it will always be false, if it is never correct, it will always be true. Using xsl:if with an XPath that starts in the root will, for the live of the transformation, always yield the same result. Not sure what you are after, so I cannot really help you further here. Normally, instead of xsl:if, we tend to use a matching template (again, yes, I know it gets boring ;).
Note: you ask something about attributes in your question, this I tried to answer in the opening paragraph (before this edit). However, there's nothing about attributes inside your code, so I don't know how to really help you.
Note on the note: LarsH suggests that you perhaps mean to ask about the match-attribute inside xsl:template. If so, the answer lies in the text above anywhere, where I talk about apply-templates and the sort. In short: the input document is processed, node by node, possibly directed by xsl:apply-templates, and it tries to find a matching template for each node it's currently at. That's all there is to it.
I want to produce a newline for text output in XSLT. Any ideas?
The following XSL code will produce a newline (line feed) character:
<xsl:text>
</xsl:text>
For a carriage return, use:
<xsl:text>
</xsl:text>
My favoured method for doing this looks something like:
<xsl:stylesheet>
<xsl:output method='text'/>
<xsl:variable name='newline'><xsl:text>
</xsl:text></xsl:variable>
<!-- note that the layout there is deliberate -->
...
</xsl:stylesheet>
Then, whenever you want to output a newline (perhaps in csv) you can output something like the following:
<xsl:value-of select="concat(elem1,elem2,elem3,$newline)" />
I've used this technique when outputting sql from xml input. In fact, I tend to create variables for commas, quotes and newlines.
Include the attribute Method="text" on the xsl:output tag and include newlines in your literal content in the XSL at the appropriate points. If you prefer to keep the source code of your XSL tidy use the entity
where you want a new line.
You can use: <xsl:text>
</xsl:text>
see the example
<xsl:variable name="module-info">
<xsl:value-of select="#name" /> = <xsl:value-of select="#rev" />
<xsl:text>
</xsl:text>
</xsl:variable>
if you write this in file e.g.
<redirect:write file="temp.prop" append="true">
<xsl:value-of select="$module-info" />
</redirect:write>
this variable will produce a new line infile as:
commons-dbcp_commons-dbcp = 1.2.2
junit_junit = 4.4
org.easymock_easymock = 2.4
IMHO no more info than #Florjon gave is needed. Maybe some small details are left to understand why it might not work for us sometimes.
First of all, the 
 (hex) or 
 (dec) inside a <xsl:text/> will always work, but you may not see it.
There is no newline in a HTML markup. Using a simple <br/> will do fine. Otherwise you'll see a white space. Viewing the source from the browser will tell you what really happened. However, there are cases you expect this behaviour, especially if the consumer is not directly a browser. For instance, you want to create an HTML page and view its structure formatted nicely with empty lines and idents before serving it to the browser.
Remember where you need to use disable-output-escaping and where you don't. Take the following example where I had to create an xml from another and declare its DTD from a stylesheet.
The first version does escape the characters (default for xsl:text)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" encoding="utf-8"/>
<xsl:template match="/">
<xsl:text><!DOCTYPE Subscriptions SYSTEM "Subscriptions.dtd">
</xsl:text>
<xsl:copy>
<xsl:apply-templates select="*" mode="copy"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()" mode="copy">
<xsl:copy>
<xsl:apply-templates select="#*|node()" mode="copy"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and here is the result:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Subscriptions SYSTEM "Subscriptions.dtd">
<Subscriptions>
<User id="1"/>
</Subscriptions>
Ok, it does what we expect, escaping is done so that the characters we used are displayed properly. The XML part formatting inside the root node is handled by ident="yes". But with a closer look we see that the newline character 
 was not escaped and translated as is, performing a double linefeed! I don't have an explanation on this, will be good to know. Anyone?
The second version does not escape the characters so they're producing what they're meant for. The change made was:
<xsl:text disable-output-escaping="yes"><!DOCTYPE Subscriptions SYSTEM "Subscriptions.dtd">
</xsl:text>
and here is the result:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE Subscriptions SYSTEM "Subscriptions.dtd">
<Subscriptions>
<User id="1"/>
</Subscriptions>
and that will be ok. Both cr and lf are properly rendered.
Don't forget we're talking about nl, not crlf (nl=lf). My first attempt was to use only cr:
 and while the output xml was validated by DOM properly.
I was viewing a corrupted xml:
<?xml version="1.0" encoding="utf-8"?>
<Subscriptions>riptions SYSTEM "Subscriptions.dtd">
<User id="1"/>
</Subscriptions>
DOM parser disregarded control characters but the rendered didn't. I spent quite some time bumping my head before I realised how silly I was not seeing this!
For the record, I do use a variable inside the body with both CRLF just to be 100% sure it will work everywhere.
You can try,
<xsl:text>
</xsl:text>
It will work.
I added the DOCTYPE directive you see here:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xsl:stylesheet [
<!ENTITY nl "
">
]>
<xsl:stylesheet xmlns:x="http://www.w3.org/2005/02/query-test-XQTSCatalog"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
This allows me to use &nl; instead of
to produce a newline in the output. Like other solutions, this is typically placed inside a <xsl:text> tag.
I second Nic Gibson's method, this was
always my favorite:
<xsl:variable name='nl'><xsl:text>
</xsl:text></xsl:variable>
However I have been using the Ant task <echoxml> to
create stylesheets and run them against files. The
task will do attribute value templates, e.g. ${DSTAMP} ,
but is also will reformat your xml, so in some
cases, the entity reference is preferable.
<xsl:variable name='nl'><xsl:text>
</xsl:text></xsl:variable>
I have found a difference between literal newlines in <xsl:text> and literal newlines using
.
While literal newlines worked fine in my environment (using both Saxon and the default Java XSLT processor) my code failed when it was executed by another group running in a .NET environment.
Changing to entities (
) got my file generation code running consistently on both Java and .NET.
Also, literal newlines are vulnerable to being reformatted by IDEs and can inadvertently get lost when the file is maintained by someone 'not in the know'.
I've noticed from my experience that producing a new line INSIDE a <xsl:variable> clause doesn't work.
I was trying to do something like:
<xsl:variable name="myVar">
<xsl:choose>
<xsl:when test="#myValue != ''">
<xsl:text>My value: </xsl:text>
<xsl:value-of select="#myValue" />
<xsl:text></xsl:text> <!--NEW LINE-->
<xsl:text>My other value: </xsl:text>
<xsl:value-of select="#myOtherValue" />
</xsl:when>
</xsl:choose>
<xsl:variable>
<div>
<xsl:value-of select="$myVar"/>
</div>
Anything I tried to put in that "new line" (the empty <xsl:text> node) just didn't work (including most of the simpler suggestions in this page), not to mention the fact that HTML just won't work there, so eventually I had to split it to 2 variables, call them outside the <xsl:variable> scope and put a simple <br/> between them, i.e:
<xsl:variable name="myVar1">
<xsl:choose>
<xsl:when test="#myValue != ''">
<xsl:text>My value: </xsl:text>
<xsl:value-of select="#myValue" />
</xsl:when>
</xsl:choose>
<xsl:variable>
<xsl:variable name="myVar2">
<xsl:choose>
<xsl:when test="#myValue != ''">
<xsl:text>My other value: </xsl:text>
<xsl:value-of select="#myOtherValue" />
</xsl:when>
</xsl:choose>
<xsl:variable>
<div>
<xsl:value-of select="$myVar1"/>
<br/>
<xsl:value-of select="$myVar2"/>
</div>
Yeah, I know, it's not the most sophisticated solution but it works, just sharing my frustration experience with XSLs ;)
I couldn't just use the <xsl:text>
</xsl:text> approach because if I format the XML file using XSLT the entity will disappear. So I had to use a slightly more round about approach using variables
<xsl:variable name="nl" select="'
'"/>
<xsl:template match="/">
<xsl:value-of select="$nl" disable-output-escaping="no"/>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:text xml:space="preserve">
</xsl:text>
just add this tag:
<br/>
it works for me ;) .