If I do something like this:
<xsl:variable name="something">
<xsl:value-of select="node1" /><br />
<xsl:value-of select="node2" /><br />
<xsl:value-of select="node3" /><br />
<xsl:value-of select="node4" /><br />
</xsl:variable>
<h1><xsl:value-of select="$something" /></h1>
The line breaks are ignored. Is that right?
I'm using xslt 1.0 if that makes any difference.
Thanks in advance.
You should be using xsl:copy-of here, not xsl:value-of because xsl:value-of only outputs the text value of a node
<h1><xsl:copy-of select="$something" /></h1>
You should use copy-of instead of value-of in order to maintain the <br/>. The reason is that the value-of looks for the text of the node that you select so if you want the text and the elements of that node you would need the copy and not the value.
Since you are using XSLT 1.0 you may also want to look into exslt:node-set for better handling of result tree fragments like this.
As a side note, your h1 is being closed by an h2 which is not valid. Hopefully that was just a copy and paste error.
Related
Given the following:
<xsl:value-of select="format-number(//items/subitem[#name = 'name']/#value, '#,###.00')" />
is it even possible to wrap the resultant decimals in a superscript tag (<sup>)?
My research, explorations, and attempts suggest that it is not, but I'm far from an expert in using XSLT.
Any suggestions, pointers, links would be greatly appreciated.
I'm stumped.
The answer is indeed no. The second argument to the format-number function is a format pattern string (in XSLT 1.0), or a picture string (in XSLT 2.0, which you can read about here: https://www.w3.org/TR/xslt20/#processing-picture-string)
One thing you can do though is something like this
<xsl:variable name="value" select="//items/subitem[#name = 'name']/#value" />
<xsl:value-of select="format-number(floor($value), '#,###')" />
<sup>
<xsl:value-of select="format-number($value - floor($value), '.00')" />
</sup>
If I have an XPath to a NodeSet - it is my understanding that the following XSLT will iterate over each node that matches the provided XPath. And the "value-of" function, matching on "." should return the data from each matching node.
<xsl:for-each select="//some/X-Path/Here/*">
<xsl:value-of select="." />
</xsl:for-each">
What if I have an xpath to a single element though? Suddenly doing a "for-each" over that single node seems silly. But I can't find a comparable "feature" in XSLT that accomplishes the same behavior - but where the input is just a single element, rathern than a collection.
<xsl:match select="//some/X-Path/Here[1]">
<xsl:value-of select="." />
</xsl:match">
Or something to this effect. Am I missing something obvious?
Well, you basically answered your question yourself. Iterating over a node set of size one may look a little strange but there are other ways to do it. One is creating a match template as you suggested and having it called through <apply-templates>. The other way is to insert the value directly as #halfbit suggested. IMHO it is very difficult to say what is the best method. It definitely depends on the context.
However, maybe two more thoughts in favor of <for-each> compared to using <value-of>.
The former only execute if the expression actually exists whereas the latter always executes. This is, of course, not not bad for the <value-of> part since it should be emtpty but it may be unhandy if it is surrounded by wrappers which might screw up your output. So in other words a
...
<xsl:for-each select="//some/X-Path/Here/*">
output something here
<xsl:value-of select="." />
output something here
</xsl:for-each">
...
has an implicit if-condition for the block, which on the other hand the construct
...
output something here
<xsl:value-of select="//some/X-Path/Here/*" />
output something here
...
does not. So, you would have to surround it by an <if> then:
...
<xsl:if test="//some/X-Path/Here/*">
output something here
<xsl:value-of select="//some/X-Path/Here/*" />
output something here
<xsl:if>
...
The other thing is that the <for-each> tag changes the context node which may be handy if you want to access more than element down the path of your XPath. So, for example,
...
<xsl:for-each select="//some/X-Path/Here/*">
<xsl:value-of select="#attr1" />
<xsl:value-of select="#attr2" />
<xsl:value-of select="#attr3" />
...
<xsl:value-of select="#attrN" />
</xsl:for-each">
...
is simply shorter (and may be easier to read) than
...
<xsl:if test="//some/X-Path/Here/*">
<xsl:value-of select="//some/X-Path/Here/*/#attr1" />
<xsl:value-of select="//some/X-Path/Here/*/#attr2" />
<xsl:value-of select="//some/X-Path/Here/*/#attr3" />
...
<xsl:value-of select="//some/X-Path/Here/*/#attrN" />
<xsl:if>
...
especially if you have a lengthy XPath expression.
I'm still learning XSLT, and have a question about the for-each loop.
Here's what I have as far as XML
<body>Here is a great URL<link>http://www.somesite.com</link>Some More Text</body>
What I'd like is if the for-each loop iterate through these chunks
1. Here is a great URL
2. http://www.somesite.com
3. Some More Text
This might be simple, or impossible, but if anyone can help me out I'd appreciate it!
Thanks,
Michael
You should be able to do so with something like the following:
<xsl:for-each select=".//text()">
<!-- . will have the value of each chunk of text. -->
<someText>
<xsl:value-of select="." />
</someText>
</xsl:for-each>
or this may be preferable because it allows you to have a single template that you can invoke from multiple different places:
<xsl:apply-templates select=".//text()" mode="processText" />
<xsl:template match="text()" mode="processText">
<!-- . will have the value of each chunk of text. -->
<someText>
<xsl:value-of select="." />
</someText>
</xsl:for-each>
xslt is pretty new for me. Is it possible to do something similar to my code below. I know it is possible in other template languages.
<div class="<xsl:if test="position()=1">myclass</xsl:if>">Hello</div>
You could wrap an xsl:attribute in an xsl:if...
<div>
<xsl:if test="position()=1">
<xsl:attribute name="class">myclass</xsl:attribute>
</xsl:if>
<xsl:text>Hello</xsl:text>
</div>
Also, in XSLT 2.0, you can write the xsl:attribute like this:
<xsl:attribute name="class" select="'myClass'"/>
Another XSLT 2.0 option, if you don't mind having an empty class="", is to use an if in an AVT (Attribute Value Template):
<div class="{if (position()=1) then . else ''}">...</div>
The then may vary depending on context.
It should be something like this:
<xsl:variable name="myclass" select="variablenode" />
<div class="adf">
<xsl:if test="yournode[position()=1]">
<xsl:value-of select="$myclass"/>
</xsl:if>
Hello</div>
But please give us your source XML, the XSLT you have so far and the expected output. Otherwise we can only guess.
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.