Unknown function saxon:parse-html when compiling stylesheet - xslt

I am working on an XSL transformation on Oxygen using the Saxon-EE 10.3 transformer. I want to use the compiled stylesheet (sef.json) later on my website with Saxon-JS 2.
Inside of the XSL transformation I am using the saxon:parse-html function with the saxon namespace declared as following:
<xsl:stylesheet xmlns:prop="http://saxonica.com/ns/html-property"
xmlns:xhtml="http://www.w3.org/1999/xhtml"
xmlns:style="http://saxonica.com/ns/html-style-property"
xmlns:saxon="http://saxon.sf.net/"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT"
xmlns:js="http://saxonica.com/ns/globalJS"
exclude-result-prefixes="xs prop ixsl js style saxon xhtml"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xpath-default-namespace="http://www.tei-c.org/ns/1.0"
xmlns="http://www.tei-c.org/ns/1.0">
and the function is called this way:
<xsl:call-template name="nameTemplate">
<xsl:with-param name="html">
<xsl:copy-of select="saxon:parse-html(var)"></xsl:copy-of>
</xsl:with-param>
</xsl:call-template>
I tried to compile the stylesheet through this command:
xslt3 -xsl:test.xsl -export:test.sef.json -t
but I encounter the following error:
Failed to compile stylesheet: Static error in XPath on line 147 in Oxygen/Test.xsl {saxon:parse-html(?Text)}: Unknown function Q{http://saxon.sf.net/}parse-html()
Error Q{http://www.w3.org/2005/xqt-errors}XPST0017 at xpath.xsl#963
Failed to compile stylesheet
Error Q{http://www.w3.org/2005/xqt-errors}XPST0017 at xpath.xsl#963
Static error in XPath on line 147 in Oxygen/Test.xsl {saxon:parse-html(?Text)}: Unknown function Q{http://saxon.sf.net/}parse-html()
The transformation works without problem inside Oxygen though.

You might need to call into JavaScript e.g. set up a script element
<script>
function parseHTML(html) { return new DOMParser().parseFromString(html, 'text/html'); }
</script>
in your HTML document and then inside of XSLT with Saxon JS 2 in the browser you should be able to use e.g.
ixsl:window() => ixsl:get('parseHTML') => ixsl:apply([var])
instead of saxon:parse-html(var), with a namespace declaration of xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT" in your XSLT.
Another way to not require you to set up the script code in addition to the XSLT code is to use ixsl:eval to run the JavaScript directly from XSLT in Saxon-JS 2; I have set up an example at https://martin-honnen.github.io/saxon-js-parse-html-test/html/test-saxon-parse-html2.html which basically uses an implementation
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:saxon="http://saxon.sf.net/"
xmlns:ixsl="http://saxonica.com/ns/interactiveXSLT"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:function name="saxon:parse-html" as="document-node()" use-when="system-property('xsl:product-name') = 'Saxon-JS'">
<xsl:param name="html" as="xs:string"/>
<xsl:sequence select="ixsl:eval('new DOMParser()') => ixsl:call('parseFromString', [$html, 'text/html'])"/>
</xsl:function>
</xsl:stylesheet>
of the XSLT 3 module https://github.com/martin-honnen/saxon-js-parse-html-test/blob/master/xslt/override-saxon-parse-html2.xsl.
You can xsl:import that in your other XSLT code, as done in https://github.com/martin-honnen/saxon-js-parse-html-test/blob/master/xslt/test-override-saxon-parse.xsl with e.g. <xsl:import href="override-saxon-parse-html2.xsl"/> and call e.g. saxon:parse-html(.).
I managed to compile that code to an SEF file with the settings xslt3 -xsl:test-override-saxon-parse.xsl -nogo -export:test-override-saxon-parse. -sef.json -ns:"##html5" and that way the HTML page https://martin-honnen.github.io/saxon-js-parse-html-test/html/test-saxon-parse-html2.html can simply run that XSLT with
SaxonJS.transform({
stylesheetLocation: '../xslt/test-override-saxon-parse.sef.json',
sourceLocation: '../xml/sample2.xml',
destination: 'appendToBody'
}, 'async')
As an alternative, you could import the pure XSLT 2 HTML parser that David Carlisle has somewhere on GitHub into your XSLT code.

Related

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.

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.

Value-of select in <a href=> (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.

xsl number()-function cannot convert

I am using fop 1.0 in a .NET application.
My stylesheet looks like
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet ... version="1.0" ...>
...
<xsl:param name="fromPerson" select="'1'"/>
<xsl:param name="toPerson" select="'1'"/>
...
<xsl:template match="*[local-name()='Person']">
<xsl:if test="(position() >= number($fromPerson))>
...
</xsl:if>
</xsl:template>
The problem is that the function number() fails with:
Zeilennummer221; Spaltennummer267; #UNKNOWN (cli.System.Int32) kann nicht in eine Zahl
konvertiert werden!
(Position des Fehlers unbekannt)org.apache.fop.fo.ValidationException: "fo:root"
is missing child elements. Required content model: (layout-master-set, declarations?,
bookmark-tree?, (page-sequence|fox:external-document)+) (Keine Kontextinformationen
verf³gbar)
Important:
#UNKNOWN (cli.System.Int32) cannot be converted into an integer
btw: I tested it by printing out the value with <xsl:message>
Any idea why this brings out the error?
The types of value that can be passed to global stylesheet parameters depend on the API of the processor. I guess you are using a processor whose API does not allow values of type cli.System.Int32.
Indeed, the types accepted by the Microsoft API are listed here:
http://msdn.microsoft.com/en-us/library/system.xml.xsl.xsltargumentlist.addparam.aspx
I think you need an <xsl:variable>, not an <xsl:param>. Parameters are subelements of the <xsl:template> element (or in XSLT 2 of an <xsl:function> element.

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