Value-of select in <a href=> (XSLT) - xslt

I try to concstruct link with
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:value-of select="concat('file:///', substring-before('%RolesPath%', 'roles'),'Flores.chm')"/>
</xsl:attribute>
Help
</xsl:element>
but I get error:
File file:///Flores.chm not found
I'm pretty sure, that variable %RolesPath% works fine. I'm using it in code normally. And if I use in code only
<xsl:value-of select="concat('file:///', substring-before('%RolesPath%', 'roles'),'Flores.chm')"/>
I get
file:///C:\Flores\Flores.chm
which is right path. Where I'm doing mistake please?
edit. %RolesPath% stores path to specify folder of program, which works with this code. In my case %RolesPath% stores "C:\Flores\roles\".
To specify my problem. I need open file(Flores.chm) in root folder of program. Program can be install everywhere in PC and prapably only way, how I can get the path is via %RolesPath%.

What you are passing to substring-before() is just a string ('%RolesPath%'). It appears that you are trying to use a Windows environment variable. This isn't going to work the way you're using it.
I think you have 2 options:
Option 1
Pass the value of the environment variable as an xsl:param when you call the stylesheet. This would work in either XSLT 1.0 or 2.0.
You would need the xsl:param:
<xsl:param name="RolesPath"/>
and this is how you would reference it:
<a href="{concat('file:///', substring-before($RolesPath, 'roles'),'Flores.chm')}"/>
Option 2
Use the environment-variable() function. This would only work with an XSLT 3.0 processor, such as Saxon-PE or EE.
Example:
<a href="{concat('file:///', substring-before(environment-variable('RolesPath'), 'roles'),'Flores.chm')}"/>
Here's another example of environment-variable() to show the function actually working:
XSLT 3.0
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<environment-variable name="TEMP" value="{environment-variable('TEMP')}"/>
</xsl:template>
</xsl:stylesheet>
Output (when applied to any well-formed XML)
<environment-variable name="TEMP" value="C:\Users\dhaley\AppData\Local\Temp"/>

Use this shorter expression:
<a href="file:///{substring-before($RolesPath, 'roles')}Flores.chm"/>
where $RolesPath is passed as an external, global parameter to the transformation.
How exactly to pass an external parameter to the transformation varies from one XSLT processor to another -- read your XSLT processor documentation. Some XSLT processors also allow string-typed parameters to be passed to the transformation from a command-line execution utility.

Related

Parameterize path to import a xslt from other xslt

I am trying to parametrize a path to include a xslt file into other, I have been trying with the way described here:
<xsl:param name="basedir" />
<xsl:include href="{$basedir}/team-menu.xsl" />
and call to the xslt doing:
xsltproc --stringparam basedir style example.xslt example.xml
But no way. I am not abel to do it work. It seems like if the xsl:output element was related because the only way I could make it "work" was this:
<xsl:include href="the/path/to/file/team-menu.xsl" />
<xsl:output method="xml" indent="yes"/>
<xsl:param name="basedir" />
I mean... putting the param after the output and the include before. Big problem: I can´t use the variable basedir.
Any way to do this??
Thank you in advance.
In XSLT 3.0 you can do this provided the parameters are declared as static, which means the values have to be supplied at compile time. You also need to prefix the attribute with "_":
<xsl:param name="basedir" static="yes"/>
<xsl:include _href="{$basedir}/team-menu.xsl" />
The reason it doesn't work with ordinary variables should be fairly obvious: variable values aren't known until run-time, and you can't start execution until you have found all the source code making up the stylesheet.
What you can do with earlier releases of XSLT, depending on the processor, is to redirect xs:include/xs:import URIs from the API level, for example (on Java) by using a user-supplied URIResolver.

Passing a node as parameter to a XSL stylesheet

I need to pass a node as a parameter to an XSL stylesheet. The issue is that the parameter gets sent as a string. I have seen the several SO questions regarding this topic, and I know that the solution (in XSLT 1.0) is to use an external node-set() function to transform the string to a node set.
My issue is that I am using eXist DB I cannot seem to be able to get its XSLT processor to locate any such function. I have tried the EXSLT node-set() from the namespace http://exslt.org/common as well as both the Saxon and Xalan version (I think eXist used to use Xalan but now it might be Saxon).
Are these extensions even allowed in the XSLT processor used by eXist? If not, is there something else I can do?
To reference or transform documents from the database, you should pass the path as a parameter to the transformation, and then refer to it using a parameter and variable
(: xquery :)
let $path-to-document := "/db/test/testa.xml"
let $stylesheet :=
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="source" required="no"/>
<xsl:variable name="error"><error>doc not available</error></xsl:variable>
<xsl:variable name="theDoc" select="if (doc-available($source)) then doc($source) else $error"/>
<xsl:template match="/">
<result><xsl:value-of select="$source"/> - <xsl:value-of select="node-name($theDoc/*)"/></result>
</xsl:template>
</xsl:stylesheet>
return transform:transform(<dummy/>,$stylesheet, <parameters><param name="source" value="xmldb:exist://{$path-to-document}"/></parameters>)
As per Martin Honnen's comments I don't think it is possible to pass an XML node via the <parameters> structure of the transform:transform() function in eXist. The function seems to strip away any XML tags passed to it as a value.
As a workaround I will wrap both my input XML and my parameter XML into a root element and pass that as input to the transform function.

XslCompiledTransform fails with stack overflow

I have a large XSLT file with 805 templates that, depending on the system and environment, fails with a stack overflow.
The development environment is Windows 7, InfoPath 2010 and C#. Although .NET 4.0 is installed, this version of InfoPath uses .NET 2.0.
The routine being used is:
private void TransformXML(String inputFileName, String transformFileName, String outputFileName)
{
CorralLog(String.Concat("Transform with ", transformFileName, ": ", inputFileName, " -> ", outputFileName));
using (XmlReader inputFile = XmlReader.Create(inputFileName, null))
{
XslCompiledTransform transform = new XslCompiledTransform(true);
XsltSettings settings = new XsltSettings(true, false);
transform.Load(transformFileName, settings, null);
using(XmlWriter outputFile = XmlWriter.Create(outputFileName))
{
filesToDelete.Add(outputFileName);
transform.Transform(inputFile, outputFile);
}
}
}
I can see some possibilities:
Increase the memory available to the 'transform.Load' command
Start a separate thread with more memory
Start a separate thread and execute the transform with 'msxsl.exe' (which always works)
Split up the XSLT file into smaller pieces and do a transformation multiple times
Does anyone have advice on which option to choose? Or any other suggestions?
Paul
The XLST file in question takes some elements from an XML file, changes the name for some elements, and produces another XML file. There are about 800 elements, each with its own template.
This code shows the three templates at the beginning of the XSLT file and two templates for copying elements: one for copying an element as is, and the other changing the name of the element. All subsequent templates are formatted in one of these two ways.
Is this syntax is causing recursion, or is the number of templates causing the stack overflow. (We have avoided the problem, but perhaps not solved it, by running this transform in a new thread.)
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:my="http://schemas.microsoft.com/office/infopath/2003/myXSD/2011-03-16T10:53:27">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*">
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="/SAN/ClientProfiles/ClientProfile">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="/SAN/ClientProfiles/ClientProfile/Name">
<CompanyName>
<xsl:apply-templates/>
</CompanyName>
</xsl:template>
See whether the suggestions in http://blogs.msdn.com/b/xmlteam/archive/2011/09/26/effective-xml-part-5-something-went-really-wrong-outofmemoryexception-and-stackoverflowexception-thrown-when-using-xslcompiledtransform.aspx help avoid the problem.
The most likely cause of a stack overflow is deep recursion in your XSLT code. I would take a look at the relevant templates and see if they can be written some other way, e.g. to use divide-and-conquer recursion rather than head-tail recursion, or perhaps to take advantage of XSLT 2.0 - there are a couple of good XSLT 2.0 processors for .NET.

Can an XSLT insert the current date?

A program we use in my office exports reports by translating a XML file it exports with an XSLT file into XHTML. I'm rewriting the XSLT to change the formatting and to add more information from the source XML File.
I'd like to include the date the file was created in the final report. But the current date/time is not included in the original XML file, nor do I have any control on how the XML file is created. There doesn't seem to be any date functions building into XSLT that will return the current date.
Does anyone have any idea how I might be able to include the current date during my XSLT transformation?
XSLT 2
Date functions are available natively, such as:
<xsl:value-of select="current-dateTime()"/>
There is also current-date() and current-time().
XSLT 1
Use the EXSLT date and times extension package.
Download the date and times package from GitHub.
Extract date.xsl to the location of your XSL files.
Set the stylesheet header.
Import date.xsl.
For example:
<xsl:stylesheet version="1.0"
xmlns:date="http://exslt.org/dates-and-times"
extension-element-prefixes="date"
...>
<xsl:import href="date.xsl" />
<xsl:template match="//root">
<xsl:value-of select="date:date-time()"/>
</xsl:template>
</xsl:stylesheet>
Do you have control over running the transformation? If so, you could pass in the current date to the XSL and use $current-date from inside your XSL. Below is how you declare the incoming parameter, but with knowing how you are running the transformation, I can't tell you how to pass in the value.
<xsl:param name="current-date" />
For example, from the bash script, use:
xsltproc --stringparam current-date `date +%Y-%m-%d` -o output.html path-to.xsl path-to.xml
Then, in the xsl you can use:
<xsl:value-of select="$current-date"/>
For MSXML parser, try this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:my="urn:sample" extension-element-prefixes="msxsl">
<msxsl:script language="JScript" implements-prefix="my">
function today()
{
return new Date();
}
</msxsl:script>
<xsl:template match="/">
Today = <xsl:value-of select="my:today()"/>
</xsl:template>
</xsl:stylesheet>
Also read XSLT Stylesheet Scripting using msxsl:script and Extending XSLT with JScript, C#, and Visual Basic .NET
...
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:local="urn:local" extension-element-prefixes="msxsl">
<msxsl:script language="CSharp" implements-prefix="local">
public string dateTimeNow()
{
return DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ssZ");
}
</msxsl:script>
...
<xsl:value-of select="local:dateTimeNow()"/>
Late answer, but my solution works in Eclipse XSLT. Eclipse uses XSLT 1 at time of this writing. You can install an XSLT 2 engine like Saxon. Or you can use the XSLT 1 solution below to insert current date and time.
<xsl:value-of select="java:util.Date.new()"/>
This will call Java's Data class to output the date. It will not work unless you also put the following "java:" definition in your <xsl:stylesheet> tag.
<xsl:stylesheet [...snip...]
xmlns:java="java"
[...snip...]>
I hope that helps someone. This simple answer was difficult to find for me.
format-date(current-date(), '[M01]/[D01]/[Y0001]') = 09/19/2013
format-time(current-time(), '[H01]:[m01] [z]') = 09:26 GMT+10
format-dateTime(current-dateTime(), '[h1]:[m01] [P] on [MNn] [D].') = 9:26 a.m. on September 19.
reference: Formatting Dates and Times using XSLT 2.0 and XPath

Xslt transform on special characters

I have an XML document that needs to pass text inside an element with an '&' in it.
This is called from .NET to a Web Service and comes over the wire with the correct encoding &
e.g.
T&O
I then need to use XSLT to create a transform but need to query SQL server through a SP without the encoding on the Ampersand e.g T&O would go to the DB.
(Note this all has to be done through XSLT, I do have the choice to use .NET encoding at this point)
Anyone have any idea how to do this from XSLT?
Note my XSLT knowledge isn’t the best to say the least!
Cheers
<xsl:text disable-output-escaping="yes">&<!--&--></xsl:text>
More info at: http://www.w3schools.com/xsl/el_text.asp
If you have the choice to use .NET you can convert between an HTML-encoded and regular string using (this code requires a reference to System.Web):
string htmlEncodedText = System.Web.HttpUtility.HtmlEncode("T&O");
string text = System.Web.HttpUtility.HtmlDecode(htmlEncodedText);
Update
Since you need to do this in plain XSLT you can use xsl:value-of to decode the HTML encoding:
<xsl:variable name="test">
<xsl:value-of select="'T&O'"/>
</xsl:variable>
The variable string($test) will have the value T&O. You can pass this variable as an argument to your extension function then.
Supposing your XML looks like this:
<root>T&O</root>
you can use this XSLT snippet to get the text out of it:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:template match="root"> <!-- Select the root element... -->
<xsl:value-of select="." /> <!-- ...and extract all text from it -->
</xsl:template>
</xsl:stylesheet>
Output (from Saxon 9, that is):
T&O
The point is the <xsl:output/> element. The defauklt would be to output XML, where the ampersand would still be encoded.