Project

Profile

Help

Support #5928

closed

Using compiled stylesheets with fn:transform

Added by Karim Ratib about 1 year ago. Updated 11 months ago.

Status:
Closed
Priority:
Low
Assignee:
-
Category:
-
Sprint/Milestone:
-
Start date:
2023-03-21
Due date:
% Done:

0%

Estimated time:
Applies to JS Branch:
Fix Committed on JS Branch:
Fixed in JS Release:
SEF Generated with:
Platforms:
Company:
-
Contact person:
-
Additional contact persons:
-

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?

Actions #1

Updated by Martin Honnen about 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.

Actions #2

Updated by Karim Ratib about 1 year ago

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?

Actions #3

Updated by Michael Kay about 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.

Actions #4

Updated by Martin Honnen about 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();
Actions #5

Updated by Martin Honnen about 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();
Actions #6

Updated by Karim Ratib about 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.

Actions #7

Updated by Martin Honnen about 1 year ago

Wait for Norm or Debbie from Saxonica to tell you more whether there is an option.

Actions #8

Updated by Debbie Lockett 12 months 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.

Actions #9

Updated by Karim Ratib 12 months ago

Thanks, this worked perfectly. And I see a 3-fold increase in processing speed!

Feel free to close this.

Actions #10

Updated by Michael Kay 12 months ago

Thanks for the performance feedback: so often people only tell us when there is bad news!

Actions #11

Updated by Karim Ratib 11 months 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?

Actions #12

Updated by Michael Kay 11 months 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.

Actions #13

Updated by Karim Ratib 11 months ago

Using option -relocate:on worked as described. Thanks!

Actions #14

Updated by Michael Kay 11 months ago

  • Status changed from New to Closed

Please register to edit this issue

Also available in: Atom PDF Tracking page