How do I use the value of XSLT variables? - xslt

I am trying to learn XSLT. I am simply getting crazy. Variables should be declared within xsl:variables entity and instantiated with their names having the $ symbol just before them (like perl variables), right? Why on earth this code:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:variable name="color" select="'red'" />
<p>$color</p>
</xsl:template>
</xsl:stylesheet>
results in the literal string: "$color" being written parsing a simple non empty xml document using the msxsl parser? Many thanks

Use <xsl:value-of select="$color"/> instead of writing $color directly to the document.
See also this question.

Also, within attributes you can get to the values directly like this:
<span style="color:{$color}" />

Why on earth this code: ...
<xsl:variable name="color"
select="'red'" />
<p>$color</p>
results in the literal string:
"$color" being written
Because this code means: output the string $color as the text node child of the <p> element.
To output the value of the $color xsl:variable use one of these:
<xsl:value-of select="$color"/>
<xsl:copy-of select="$color"/>

A good rule of thumb for xslt: if it's not in an <xsl:foo> tag, it's not code. It's output.

Related

Is it possible to call a program during a XSL transformation if something match a template

Is it possible to call a program (e.g. a Perl script) during a XSL transformation if something match a template
<xsl:template match="row">
<p>
<xsl:value-of select="a"/>
</p>
<p>
<!--
I have elements 'b' and 'c' inside a 'row' as well.
I want to execute 'perl "value_of_b" "value_of_c"' and insert output here'
-->
</p>
</xsl:template>
Q.: Is it possible to call a program during a XSL transformation if something match a template
The answer depend on your xslt processor.
For perl have a look to XML::LibXSLT register_function.
Registers an XSLT extension function mapped to the given URI. For example:
XML::LibXSLT->register_function("urn:foo", "bar",
sub { scalar localtime });
Depends. I run something like this inside OxygenXML successfully.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:os-command="java:java.lang.Runtime"
exclude-result-prefixes="xs os-command"
version="2.0">
<xsl:template match="row">
<p>
<xsl:value-of select="a"/>
</p>
<!-- build the OS command string -->
<xsl:variable name="cmd-string" select="concat('perl ',a,' ',b)"/>
<xsl:value-of
select="os-command:exec(os-command:getRuntime(),$cmd-string)"/>
</xsl:template>
</xsl:stylesheet>
I have not tried it outside Oxygen. And also, I was not successful getting this to execute a bash script. Perl may pose a similar problem.
Here's a link to the original attempt at this.

How can I replace text with angle bracket without parsing the replace value?

I have this:
replace("Both cruciate ligaments are well visualized and are intact.",
".",
".<br>")
But I do not want to output the escaped angle brackets but the actual brackets. when I run the code I get :
Both cruciate ligaments are well visualized and are intact.<br>
I want:
Both cruciate ligaments are well visualized and are intact.<br>
How can I achieve that? I cannot use the angle bracket directly as replace value since I get an error.
EDIT
I have a stylesheet that takes in a text file that is injected into a HTML file (coming from the stylesheet). I take an XML (Clinical document) and a text file and merge them together with the stylesheet. So for example I have:
RADIOLOGY REPORT
NAME: JOHN, DOE
DoB: 1982-02-25
Injected text goes here
The text has to wrap on carriage return and has to wrap at a word level. I did manage to do the latter but I did not find a way to the line breaks. I thought of finding 'LF' in the file an replace with <BR> so that once the page is rendered I get to see the line breaks.
You need to use xsl:analyze-string if you want to output nodes and not simply strings. Here is an example:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html"/>
<xsl:template match="text">
<xsl:analyze-string select="." regex="\.">
<xsl:matching-substring>
<xsl:value-of select="."/><br/>
</xsl:matching-substring>
<xsl:non-matching-substring>
<xsl:value-of select="."/>
</xsl:non-matching-substring>
</xsl:analyze-string>
</xsl:template>
</xsl:stylesheet>
With the input being
<text>Both cruciate ligaments are well visualized and are intact.</text>
the transformation result is
Both cruciate ligaments are well visualized and are intact.<br>
Martin Honnen's answer is a perfectly good way to do this.
Using a simple template to find the text in question is another way:
<xsl:variable name="magic-string"
select='"Both cruciate ligaments are well visualized and are intact."'/>
...
<xsl:template match="text()
[contains(.,$magic-string)]">
<xsl:value-of select="substring-before(.,$magic-string)"/>
<xsl:value-of select="$magic-string"/>
<br/>
<xsl:value-of select="substring-after(.,$magic-string)"/>
</xsl:template>
In either case, use the HTML output method to serialize the empty br element as <br> instead of as <br/>.
Note: I'm assuming here that you want a br after this particular sentence, not that you want one after each occurrence of full stop, which is how Martin Honnen appears to have interpreted the question.

Overwrite the variable in xsl

I just need to overwrite the variable in xsl
Example:
x=0
if x=0
then
x=3
I need to change the value of variable.
I am very new to xsl, please help me how to achieve this. This may be silly but I don't have any idea..
I just need to overwrite the variable in xsl
Example x=0 if x=0 then x=3
XSLT is a functional language and among other things this means that a variable, once defined cannot be changed.
Certainly, this fact doesn't mean that a given problem cannot be solved using XSLT -- only that the solution doesn't contain any modifications of variable values, once defined.
Tell us what is your specific problem, and many people will be able to provide an XSLT solution :)
As other comments have noted, variables in XSLT cannot be modified once they are set. The easiest way I've found to do this is to nest variables inside each other.
<xsl:variable name="initial_condition" select="VALUE"/>
Later
<xsl:variable name="modified_condition" select="$initial_condition + MODIFIER"/>
Some of our xsl has whole reams of nested calculations which really should be in the business logic which produces the source XML. Due to a period of time where there was no developer / time to add this business logic, it was added as part of the presentation layer.
It becomes extremely hard to maintain code like this, especially considering you've probably got control flow considerations to make. The variable names end up being very convoluted and readability drops through the floor. Code like this should be a last resort, it's not really what XSLT is designed for.
The <xsl:variable> in xslt is not actual a variable. Which means it can not be changed after you have defined it and you can use it like this:
lets say we have this xml with name test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<client-list>
<client>
<name>person1</name>
</client>
<client>
<name>person2</name>
</client>
<client>
<name>person3</name>
</client>
</client-list>
and we want to transform it to csv-like (comma separated values) but replacing the person1 with a hidden person with name person4. Then lets say we have this xml with name test.xsl which will be used to transform the test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="hiddenname">person4</xsl:variable>
<!-- this template is for the root tag client-list of the test.xml -->
<xsl:template match="/client-list">
<!-- for each tag with name client you find, ... -->
<xsl:for-each select="client">
<!-- if the tag with name -name- don't have the value person1 just place its data, ... -->
<xsl:if test="name != 'person1'">
<xsl:value-of select="name"/>
</xsl:if>
<!-- if have the value person1 place the data from the hiddenperson -->
<xsl:if test="name = 'person1'">
<xsl:value-of select="$hiddenname"/>
</xsl:if>
<!-- and place a comma -->
<xsl:text>,</xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
the results will be
person4,person2,person3,
I hope this will help you.

Why doesn't it see the variable inside element declaration?

The following code gives an error:
[1.0-ml] XDMP-UNDVAR: (err:XPST0008) Undefined variable $ename
However if I change the xsl:element to <xsl:element name="yyyXXX"> it shows that it sees $ename in value-of, i.e.
<yyyXXX>zzz</yyyXXX>
The stylesheet below works just fine in Saxonica 9.x
Thanks.
xquery version "1.0-ml";
xdmp:xslt-eval(
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xdmp="http://marklogic.com/xdmp"
extension-element-prefixes="xdmp"
version="2.0">
<xsl:template match="/">
<xsl:variable name="ename" select="'zzz'"/>
<xsl:element name="yyy{$ename}">
<xsl:value-of select="$ename"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
,document{ <doc/> })
You need two sets of curly brackets around your element name, e.g. yyy{{$ename}}. That tells the outer XQuery to treat those as literal curly brackets in the XSLT. If you were invoking the XSLT from an external document (e.g. with xdmp:xslt-invoke) you wouldn't need the extra brackets; this is equivalent to your Saxon test. The ability to evaluate XQuery to dynamically create XSLT is pretty powerful, but does impose a little more cognitive overhead.

Disable output escaping not working for attribute in xslt

I have the following xml-node :
<name>P & P</name>
And following XSL
<a href="example.htm" >
<xsl:attribute name="title">
<xsl:value-of select="name" disable-output-escaping="yes"></xsl:value-of>
</xsl:attribute>
<xsl:value-of select="name" disable-output-escaping="yes"></xsl:value-of>
</a>
That compiles to this HTML
<a href="example.com" title="P &amp; P">
P & P
</a>
So the non-escaping worked for the value (the text between <A></A>) but not for the attribute.
Am I missing something here?
Thanks!
From an OP's comment:
I need this xml (P & P) in the title attribute of an HTML tag. A
better solution is most welcome!
What you need to generate can be done perfectly without D-O-E.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<a href="example.htm" title="{.}">
<xsl:value-of select="."/>
</a>
</xsl:template>
</xsl:stylesheet>
When applied on the following XML document:
<t>P & P</t>
the wanted, correct result is produced:
P & P
I've been looking around and I guess this is why : (if I understand correctly) ?
Out of the specs : (http://www.w3.org/TR/xslt)
It is an error for output escaping to be disabled for a text node that
is used for something other than a text node in the result tree. Thus,
it is an error to disable output escaping for an xsl:value-of or
xsl:text element that is used to generate the string-value of a
comment, processing instruction or attribute node; it is also an error
to convert a result tree fragment to a number or a string if the
result tree fragment contains a text node for which escaping was
disabled. In both cases, an XSLT processor may signal the error; if it
does not signal the error, it must recover by ignoring the
disable-output-escaping attribute.
So disabling output for escaping an attribute is just not possible apparantly. The workaround that I see is to build a string 'by hand' as XSL - How to disable output escaping for an attribute?
Still hard to believe that I'm not missing sth. trivial here.