XSLT functions and namespaces - xslt

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,

Related

How to process HTML entities in XSLT

I am trying to transform XHTML that contains the entity. Saxon complains that the entity is not defined. How can I define it?
Is it possible to add the entity definition at the beginning of the stylesheet? As suggested
here:
http://s-n-ushakov.blogspot.com/2011/09/xslt-entities-java-xalan.html
or here:
Using an HTML entity in XSLT (e.g. )
My puny attempt, ignored by Saxon, was to add the following to the beginning of the XSLT:
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE stylesheet [
<!ENTITY nbsp " ">
]>
I am using Saxon 9.9 PE.
The HTML I am trying to transform is a complete document, not just a fragment.
One possibility is to pass the URL of the XHTML to the XSLT as a parameter, which would read the XHTML as text using the unparsed-text() function, expand the entity reference using the replace() function, and parse the result using the parse-xml() function. e.g.
<xsl:template name="xsl:initial-template">
<xsl:param name="source"/>
<xsl:apply-templates select="
$source
=> unparsed-text()
=> replace('&nbsp;', '&#x000A0;')
=> parse-xml()
"/>
</xsl:template>
If the input document contains an entity reference that isn't declared in the DOCTYPE declaration, then it isn't a well-formed XML document, and therefore it isn't a well-formed XHTML document; and if it isn't well-formed, then Saxon can't handle it.
It would be best to look at the processing workflow that generated this ill-formed document and fix it so the documents it produces are well-formed.
If you can't do that, then you might be able to parse it as HTML. Saxon has an extension function saxon:parse-html(); or if your application is in Java then you could create a SAXSource that uses validator.nu as its XMLReader.
You should consider using the tool Tidy and convert html files into xhtml. It corrects all such things.
Just run tidy with the argument -asxml.

Read service response(json format) inside xslt 2.0

Need to read a service response inside xslt which is of format :
servicedata={"statusCode":200,"loginMessage":"Welcome User"}
its key value pair, where i can get the value of 'servicedata' but further need to get the status code value for some condition checking.
Using version 2.0 for the xslt
read few questions which suggested to use parse-json(), json-to-xml with 3.0 version of xslt, but didn't help.
Question: first using version 2.0 only can I retrieve the value for the 'statusCode' and how, if not guide me how to achieve it.
If you can move to Saxon 9.7 HE then you could use json-to-xml as follows for instance:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
exclude-result-prefixes="xs math"
version="3.0">
<xsl:template match="data">
<xsl:value-of select="json-to-xml(substring-after(., 'servicedata='))//*[#key = 'statusCode']" xpath-default-namespace="http://www.w3.org/2005/xpath-functions"/>
</xsl:template>
</xsl:stylesheet>
which for the input
<data>servicedata={"statusCode":200,"loginMessage":"Welcome User"}</data>
outputs 200.
I have not checked whether earlier versions of Saxon 9 also support that.

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

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).

using xsl:param variable in xs:schema element output

I am trying to output the following line from an XSLT script. It is the first line just after xsl:template match="/". What I am trying to do is to transform XML document into an XML schema and need to output the xs:schema tag in particular way.
<xs:schema xmlns:ed="http://test1" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" targetNamespace="{$ns_name}" xmlns:tns="{$ns_name}" elementFormDefault="qualified" attributeFormDefault="unqualified" xsi:schemaLocation="http://test1 file://XmlSchemaAppinfo.xsd">
the $ns_name is a xsl:param name="ns_name". It is resolved in the targetNamespace="{$ns_name}" correctly but in the xmlns:tns="{$ns_name}" it is output literally
<xs:schema targetNamespace="akolodk" elementFormDefault="qualified" attributeFormDefault="unqualified" xsi:schemaLocation="http://test1 file://XmlSchemaAppinfo.xsd" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ed="test1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="{$ns_name}">
Namespace declarations are not treated the same as attributes, even though they look the same. The xmlns:tns will have been processed by the XML parser when the stylesheet was parsed, before it gets to the XSLT processor.
If you have XSLT 2.0 you can use
<xsl:namespace name="tns" select="$ns_name"/>
to create a namespace node in the result tree but there's no easy way I know of to generate a dynamic namespace in XSLT 1.0. You can't use xsl:attribute, the spec explicitly states that while
<xsl:attribute name="xmlns:xsl" namespace="whatever">http://www.w3.org/1999/XSL/Transform</xsl:attribute>
is not an error, it will generate an attribute, not a namespace declaration - the processor is required to ignore the xmlns prefix specified in the name and must use a different prefix to output the attribute.
If your processor supports the exslt node-set extension function then the following might work:
<xsd:schema .....>
<xsl:variable name="tnsElement">
<xsl:element name="tns:dummy" namespace="{$ns_name}"/>
</xsl:variable>
<xsl:copy-of select="exsl:node-set($tnsElement)/*/namespace::tns"/>
but again the processor is allowed to ignore the prefix of the xsl:element name attribute and use a different prefix bound to the same URI, you'll have to test it with your processor.
(and you'll have to add xmlns:exsl="http://exslt.org/common" exclude-result-prefixes="exsl" to your xsl:stylesheet element).
In XSL only some attributes can be written using attribute value templates (using the '{}' notation). In particular, xmlns attributes do not support the notation.

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