Support #5928
closed
Using compiled stylesheets with fn:transform
Fix Committed on JS Branch:
Description
I am trying to chain a sequence of xsl stylesheets using a combination of fold-left
+ nested transform
invocations, as described in this StackOverlfow post https://stackoverflow.com/a/75594839/209184.
My question is: will the xslt3
engine use the compiled stylesheet for each of the transform
invocations? If not automatically, then is there a way to instruct it to use the compiled versions?
I am not sure if you use fn:transform
with Node.js and xslt3
, whether the function does support input in the form of a compiled sef.json
format; I think in that setting the stylesheet is expected as XML/XSLT and compiled on the fly to SEF.
Martin Honnen wrote in #note-1:
I am not sure if you use fn:transform
with Node.js and xslt3
, whether the function does support input in the form of a compiled sef.json
format; I think in that setting the stylesheet is expected as XML/XSLT and compiled on the fly to SEF.
Yes, that is the question I am asking. Since it is a set of stylesheets I expect to use repeatedly, it doesn't make much sense for fn:transform
to keep recompiling it every time it is invoked.
And I realize I posted this question as an issue, but it may be better to post it in the forum?
It's fine to post the question here, and it's on our queue. We're a bit busy with a maintenance release today.
In the context of node and asynchronous programming with await the simplest to use SaxonJS.transform on an array of compiled SEFs would be along the lines of
const SaxonJS = require('saxon-js');
var inputXml = 'sample1.xml';
var xsltSefs = ['sheet1.xsl.sef.json', 'sheet2.xsl.sef.json', 'sheet3.xsl.sef.json', 'sheet4.xsl.sef.json'];
const main = async () => {
const firstResult = await SaxonJS.transform({
stylesheetLocation: xsltSefs[0],
sourceLocation: inputXml,
destination: 'application'
},
'async'
);
var sourceNode = firstResult.principalResult;
//console.log('first result', SaxonJS.serialize(sourceNode, { 'indent': true }));
for (let i = 1; i < xsltSefs.length; i++) {
var result = await SaxonJS.transform({
stylesheetLocation: xsltSefs[i],
initialSelection: sourceNode,
destination: 'application'
},
'async'
);
sourceNode = result.principalResult;
//console.log('result', i, SaxonJS.serialize(sourceNode, { 'indent': true }));
}
console.log(SaxonJS.serialize(sourceNode, { 'indent': true }));
};
main();
Here is an alternative Node.js/JavaScript approach using the Array.reduce method of JavaScript similar to the fold-left function of XPath 3.1:
const SaxonJS = require('saxon-js');
var inputXml = 'sample1.xml';
var xsltSefs = ['sheet1.xsl.sef.json', 'sheet2.xsl.sef.json', 'sheet3.xsl.sef.json', 'sheet4.xsl.sef.json'];
const main = async () => {
const result = xsltSefs.reduce(
async (sourceNode, sefURI) => {
const intermediaryResult = await SaxonJS.transform(
{
stylesheetLocation: sefURI,
sourceNode: sourceNode,
destination: 'application'
},
'async'
);
return intermediaryResult.principalResult;
},
await SaxonJS.getResource({ file: inputXml, type: 'xml' })
);
console.log(SaxonJS.serialize(result, { 'indent': true, 'method': 'xml' }));
};
main();
Thanks for this code, Martin. Love the second version!
Still, it would be great to remain within XSLT and not sacrifice performance enhancements. That's my main question here.
Wait for Norm or Debbie from Saxonica to tell you more whether there is an option.
Hi, sorry for the delay in responding.
With SaxonJS, you should be able to use the package-text
option to supply a precompiled SEF to a fn:transform
call. This is briefly described in the SaxonJS documentation at https://www.saxonica.com/saxon-js/documentation2/index.html#!conformance/xslt30 - in the notes about fn:transform
.
To use this, you need to supply the SEF as a string, so use fn:unparsed-text()
to read an sef.json
file to a string, and then supply this to fn:transform
using the package-text
option.
Note that for SaxonJ (Saxon on Java), stylesheets are cached when they are compiled, so that if a stylesheet is used repeatedly in fn:transform
calls (and it is "cachable" i.e. option static-params
is not used) then the stylesheet is indeed only compiled once. However, we do not use this caching mechanism in the SaxonJS implementation, so you are right that you need to supply the precompiled SEF to fn:transform
yourself to avoid repeated compilation.
Thanks, this worked perfectly. And I see a 3-fold increase in processing speed!
Feel free to close this.
Thanks for the performance feedback: so often people only tell us when there is bad news!
I am running into an additional snag here:
When I run the compiled spreadsheet that refers to other compiled stylesheets using fn:unparsed-text()
from a folder different than where they were compiled, the transformation fails with
[SaxonJS] FOUT1170: Cannot retrieve unparsed-text file:///path/to/original/folder/internal.sef.json
I am trying to use a relative URI such as /internal.sef.json
, but it doesn't make a difference. Is there a way to use relative URIs?
There's an option relocatable
when you compile the stylesheet to a SEF file. If you set this option, then at run-time relative URIs are interpreted relative to the deployed location of the compiled stylesheet, not relative to the original location of the source.
Using option -relocate:on
worked as described. Thanks!
- Status changed from New to Closed
Please register to edit this issue
Also available in: Atom
PDF
Tracking page