Error "cannot create ActiveX component" when using VBScript extension functions from within XSLT using msxsl:script - xslt

I need to call the vbscript externally inside the xsl. I have written a sample xml and xsl when I validate the xml against xsl in have an error of cannot create ActiveX Component.
Next I have transformed the xml against the xsl using Altova but I found XML transformation failed due to following error: Function not in namespaceerror in xpath expression,Function not in namespace.
I have inculded the xml below
XML:
<LOOP_ID>
<ID LINE="1" ID00="ISA" ISA01="00" ID02="" ID03="12" ID04="" ID05="11" ID06="111111" ID07="ZZ" ID08="11111" ID09="121005" ID10="1759" ID11="^" ID12="00501" ID13="005926056" ID14="0" ID15="P" ID16=""/>
</LOOP_ID>
xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:user="http://mycompany.com/mynamespace">
<msxsl:script language="VBScript" implements-prefix="user">
Function ValidDateFormat(sDateValue)
Dim Test As DateTime
If DateTime.TryParseExact(datetime, sFormat, New CultureInfo("en-US"), DateTimeStyles.None, Test) = True Then
Return "t"
Else
Return "f"
End If
End Function
Function checkDateLessthanCurrID(sValue)
Dim Test As DateTime
If DateTime.TryParseExact(sDate, "yyMMdd", New CultureInfo("en-US"), DateTimeStyles.None, Test) AndAlso Test < DateTime.Now Then
Return "t"
Else
Return "f"
End If
End Function
Function checkDateLessthanCurr(sValue)
Dim Test As DateTime
If DateTime.TryParseExact(datetime, "MM:dd:yyyy", New CultureInfo("en-US"), DateTimeStyles.None, Test) AndAlso Test < DateTime.Now Then
Return "t"
Else
Return "f"
End If
End Function
</msxsl:script>
<xsl:output method="text" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:apply-templates select="/LOOP_ID"/>
</xsl:template>
<xsl:template match="/LOOP_ID">
<xsl:if test="ID/#ID09 !=''">
<xsl:if test="user:checkDateLessthanCurrISA(string(ID/#ID09))='t'">
</xsl:if>
</xsl:if>
I need to validate the date in the attribute given in xml. But I cannot validate showing error cannot find the namespace.
I have also created a external dll for the project and registered using regasm But I cannot access the dll inside my xsl.
Can any one help me to solve the issue?

Your first error:
cannot create ActiveX Component
means that your code was actually executed, but failed, due to a missing ActiveX reference.
You didn't state it, but most likely you received this by using MSXML, because that is the only XSLT compiler I know of that uses ActiveX. It is also used in Internet Explorer.
Your second error:
Next I have transformed the xml against the xsl using Altova but I found XML transformation failed due to following error: Function not in namespaceerror in xpath expression,Function not in namespace.
is strange. If I run it with Altova (I use the 2013 Community version) using the /xslt commandline switch to turn off XSLT 2.0 backwards compatibility processing, it tries to parse the script block and returns the following:
Script Compile Error(s) (relative to script begin):
Line 3, Character 0: 'datetime' is a type and cannot be used as an expression.
Line 3, Character 0: 'sFormat' is not declared. It may be inaccessible due to its protection level.
Line 3, Character 0: Type 'CultureInfo' is not defined.
Line 3, Character 0: 'DateTimeStyles' is not declared. It may be inaccessible due to its protection level.
Line 12, Character 0: 'sDate' is not declared. It may be inaccessible due to its protection level.
Line 12, Character 0: Type 'CultureInfo' is not defined.
Line 12, Character 0: 'DateTimeStyles' is not declared. It may be inaccessible due to its protection level.
Line 21, Character 0: 'datetime' is a type and cannot be used as an expression.
Line 21, Character 0: Type 'CultureInfo' is not defined.
Line 21, Character 0: 'DateTimeStyles' is not declared. It may be inaccessible due to its protection level.
This suggests that the code is not correct. I think the code uses VBScript and the classes you seem to be using are VB.NET.
When running the code against Microsoft's .NET version of XSLT 1.0, I receive the similar errors as above.
It turns out that Microsoft's script parser does not take your code as VBScript (which is ActiveX), but as a VB.NET script. Which is OK, as your code looks like VB.NET.
However, it is literally full of errors. I am not going to fix every error here, but here's a shortened version of your code that runs correctly on both Altova and Microsoft XSLT versions.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:user="http://mycompany.com/mynamespace">
<xsl:strip-space elements="*"/>
<msxsl:script language="VBScript" implements-prefix="user">
Function ValidDateFormat(sDateValue As String) As String
Dim Test As DateTime
Dim sFormat As String = "MM-dd-YY"
If DateTime.TryParseExact(sDateValue, sFormat, New Globalization.CultureInfo("en-US"), Globalization.DateTimeStyles.None, Test) = True Then
Return "true"
Else
Return "false"
End If
End Function
</msxsl:script>
<xsl:output method="text" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="ID[#ID09 !='']">
<xsl:value-of select="user:ValidDateFormat(string(#ID09))"/>
</xsl:template>
</xsl:stylesheet>
The above returns true or false. I suggest you first try the code inside Visual Studio before you try it with XSLT, because inside XSLT it is quite hard to debug.
On mxsxl:script
In my previous post I was incorrect to assume that msxsl:script was not supported by Altova. Martin Honnen corrected me in the comments. It is supported and it looks like it uses the same code provider underneath as Microsoft uses.
Your original error was an ActiveX error. If you want to run your code with an ActiveX XSLT processor, you will need to do a few more things to get it running. First of all your code is not ActiveX VBScript. Second, you will need to make sure the appropriate ActiveX objects your code required can be instantiated (i.e., are on the system path).
In browsers other than Microsoft Internet Explorer and in other processors, msxsl:script is not supported. .NET scripting is not supported in any browser, use ActiveX scripting instead.
On XSLT 2.0
If you can use Altova, you are essentially using XSLT 2.0, which does not require the extensions you wrote: it can natively compare times and dates, and can get the current date and time. XSLT 2.0 processors include, but are not limited to: Altova (native), Saxon (Java and IKVM.NET), Exselt (.NET), XMLPrime (.NET).

Related

fn:transform running with default call-template invocation - no result-documents created

This transform test.xsl copies (for the purpose of a small test example) a directory of XML data by calling an identity transform for each recursively encountered XML file.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:map="http://www.w3.org/2005/xpath-functions/map"
version="3.0" exclude-result-prefixes="xsi fn xs map" expand-text="yes">
<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="currentStylesheet" as="document-node()" select="doc('identity.xsl')"/>
<xsl:template name="xsl:initial-template">
<xsl:for-each select="collection('file:///mnt/c/home/oneD/data/translatable' || '?select=*.xml;recurse=yes')">
<xsl:result-document href="{replace(document-uri(),'/data/translatable/','/translated/')}">
<xsl:sequence select=" fn:transform(map {
'stylesheet-node' : $currentStylesheet,
'source-node' : .
})?output"/>
</xsl:result-document>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
However if I invoke it from XQuery with fn:transform like so
xquery version "3.1";
fn:transform(map { 'stylesheet-node' : doc('test.xsl')})?output
none of the result-documents are created.
Is there something wrong with the invoking XQuery? I have never before tried this so it is my best attempt from reading the docs. Running on Saxon 10.3
The fn:transform function doesn't write anything to the file system, it returns a map with
one entry in the map for the principal result document, and one for
each secondary result document. The key is a URI in the form of an
xs:string value. The key for the principal result document is the base
output URI if specified, or the string "output" otherwise. The key for
secondary result documents is the URI of the document, as an absolute
URI. The associated value in each entry depends on the requested
delivery format. If the delivery format is document, the value is a
document node. If the delivery format is serialized, the value is a
string containing the serialized result
If you want to write result documents to the file system you need to do that in your code processing the map returned by the transform function, for instance with fn:put or the EXPath file module.
The option map for the transform function also has a hook post-process:
A function that is used to post-process each result document of the
transformation (both the principal result and secondary results), in
whatever form it would otherwise be delivered (document, serialized,
or raw). The first argument of the function is the key used to
identify the result in the map return by the fn:transform function
(for example, this will be the supplied base output URI in the case of
the principal result, or the string "output" if no base output URI was
supplied). The second argument is the actual value. The value that is
returned in the result of the fn:transform function is the result of
applying this post-processing.
Note that neither fn:put nor the EXPath file module is provided by Saxonica's open-source Home Edition; there is a third party file module implementation on GitHub though: https://github.com/Armatiek/saxon-extensions

implementing evaluate(XPath) in msxml

I'm writing an experiment where I take an XML file, that has XPaths embedded in it, and try to process it against another XML file with data in it, where the XPaths refer to elements within some predefined nodeset inside the data....basically binding a view to a list of data.
I've basically got it working, except how to evaluate the XPaths themselves, clearly I can do it in saxon with 3.0 (maybe I should try there first), but it would be initially convenient if this worked in msxml. I've read stuff about "extensions" and embedding javascript...but I can't really see how it would work (it didnt work for me).
any ideas?
(I could make the xslt create an xslt that creates the output, but this is a rough and ready proof of concept, and that might make my head hurt).
I can if necessary create an explicit example, but my actual scenario is quite convoluted.
Within .NET XPathNavigator has an Evaluate method: https://learn.microsoft.com/en-us/dotnet/api/system.xml.xpath.xpathnavigator.evaluate?view=netcore-3.1. You can expose it from an extension object to XslCompiledTransform.
The Mvp.Xml library does this to expose a dyn2:evaluate extension function in the namespace xmlns:dyn2="http://gotdotnet.com/exslt/dynamic" to its wrapper MvpXslTransform class around XslCompiledTransform.
The library is available on NuGet in a .NET standard 2.0 compatible (i.e. both .NET framework and .NET Core compatible) package: https://www.nuget.org/packages/Mvp.Xml.NetStandard.
Simplest code would be (using Mvp.Xml.Common.Xsl;):
var processor = new MvpXslTransform();
processor.SupportedFunctions = Mvp.Xml.Exslt.ExsltFunctionNamespace.GdnDynamic;
processor.Load("XSLTFile1.xslt");
processor.Transform(new XmlInput("XMLFile1.xml"), null, new XmlOutput(Console.Out));
where the stylesheet uses e.g. dyn2:evaluate(., path) with xmlns:dyn2="http://gotdotnet.com/exslt/dynamic" where the path child element of the context node contains an XPath expression.
The source code is at https://github.com/keimpema/Mvp.Xml.NetStandard/blob/master/library/Mvp.Xml/Exslt/GDNDynamic.cs.
In the .NET framework you can also embed .NET code directly in the XSLT and XslCompiledTransform will compile and run it with the proper XsltSettings (new XsltSettings() { EnableScript = true }):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:my-ext="http://example.com/my-ext"
exclude-result-prefixes="msxsl my-ext">
<msxsl:script implements-prefix="my-ext" language="C#">
public object evaluate(XPathNodeIterator context, string expression)
{
if (context.MoveNext()) {
return context.Current.Evaluate(expression);
}
else {
return null;
}
}
</msxsl:script>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xpath-test">
<xsl:copy>
<result>
<xsl:value-of select="my-ext:evaluate(., path)"/>
</result>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
I don't know whether Visual Studio allows you to enable that setting for its transformation menu.

XSLT functions and namespaces

I'm kind of new to XSLT, and I've gotten basic transformation done. Next I want to try out date manipulations, since my data will have timestamps. However, I can't seem to get any date functions to work, and it greatly frustrates me. I'm testing using Firefox 3.5, xsltproc 1.1.24, xalan 1.10, and XMLSpy 2009, and they all say that the functions I'm trying to use don't exist.
My xml looks like so:
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="datetime.xsl"?>
<watcher>
<event id="1" date="2009-09-04T13:49:10-0500" type="ABCD">This is a test </event>
</watcher>
</code>
My xsl looks like so:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fn="http://www.w3.org/2005/02/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:template match="event[#type='ABCD']">
<!-- Date: <xsl:value-of select="day-from-dateTime(xs:dateTime(#date))"/> -->
<!-- Date: <xsl:value-of select="day-from-dateTime(#date)"/> -->
Date: <xsl:value-of select="fn:day-from-dateTime(#date)"/>
</xsl:template>
</xsl:stylesheet>
If I make the stylesheet version 2, XMLSpy complains that it can't cast my date: XSLT 2.0 Debugging Error: Error in XPath 2.0 expression (Cast failed, invalid lexical value - xs:dateTime '2009-09-04T13:49:10-0500')
If I leave it as version 1, it complains about a different error: XSLT 1.0 Debugging Error: Error in XPath expression (Unknown function - Name and number of arguments do not match any function signature in the static context - 'day-from-dateTime')
Anytime I try to change the XSL to use a namespace, such as fn:day-from-dateTime, it refuses to work at all, with all of my parsers saying that The function number 'http://www.w3.org/2005/02/xpath-functions:day-from-dateTime' is not available and variants thereof. I know from other tests that I can use the substring() function perfectly, without needing any namespace prefix, and I believe it's in the same namespace as day-from-dateTime.
I feel like it's something incredibly easy, since all of the tutorials show functions being used, but something seems to be eluding me. Could someone show me what I'm missing?
Ouch, nasty versions thing going on here. A lot of the issues you're seeing will be because the XSLT processor you're using doesn't support XPath 2.0, which is where that day-from-dateTime function comes from.
I can get what you're trying to do to work, with a Saxon processor - Saxon-B 9.1.0.6 as my processor instead of Xalan. (Xalan appears to support XPath 1.0 only, according to the documentation)
There are a few errors in your documents:
The source document should have the timezone as 05:00, not 0500
<?xml version="1.0" encoding="UTF-8"?>
<watcher>
<event id="1" date="2009-09-04T13:49:10-05:00" type="ABCD">This is a test </event>
</watcher>
The XSLT should cast the string 2009-09-04T13:49:10-05:00 into a xs:dateTime, which is what type the argument of day-from-dateTime needs to be.
Date: <xsl:value-of select="day-from-dateTime(xs:dateTime(#date))"/>
And then it works
<?xml version="1.0" encoding="UTF-8"?>
Date: 4
Hope that helps,

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.