Support #5928
closedUsing compiled stylesheets with fn:transform
0%
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?
Updated by Martin Honnen over 1 year ago
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.
Updated by Karim Ratib over 1 year ago
Martin Honnen wrote in #note-1:
I am not sure if you use
fn:transform
with Node.js andxslt3
, whether the function does support input in the form of a compiledsef.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?
Updated by Michael Kay over 1 year ago
It's fine to post the question here, and it's on our queue. We're a bit busy with a maintenance release today.
Updated by Martin Honnen over 1 year ago
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();
Updated by Martin Honnen over 1 year ago
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();
Updated by Karim Ratib over 1 year ago
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.
Updated by Martin Honnen over 1 year ago
Wait for Norm or Debbie from Saxonica to tell you more whether there is an option.
Updated by Debbie Lockett over 1 year ago
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.
Updated by Karim Ratib over 1 year ago
Thanks, this worked perfectly. And I see a 3-fold increase in processing speed!
Feel free to close this.
Updated by Michael Kay over 1 year ago
Thanks for the performance feedback: so often people only tell us when there is bad news!
Updated by Karim Ratib over 1 year ago
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?
Updated by Michael Kay over 1 year ago
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.
Updated by Karim Ratib over 1 year ago
Using option -relocate:on
worked as described. Thanks!
Please register to edit this issue
Also available in: Atom PDF Tracking page