Bug #6132
opendocumentPool passed to SaxonJS.transform doesn't seem to be used if the stylesheet uses fn:transform or xsl:evaluate
0%
Description
For SaxonJS.transform, I can pass a documentPool
, mapping URLs to resources, to allow preloading resources or loading resources from a string.
An example of that is e.g. a stylesheet (shown here as XSLT, for SaxonJS it will be compiled to sef.json)
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
expand-text="yes">
<xsl:template name="xsl:initial-template">
<xsl:sequence select="doc('doc1.xml') => serialize(map{'method':'xml'})"/>
</xsl:template>
</xsl:stylesheet>
and (for Node.js) Javascript code like
const path = require('path');
const url = require('url');
const SaxonJS = require('saxon-js');
var xmlResource1 = SaxonJS.getResource({'type': 'xml', 'text' : '<root>foo</root>' });
var doc1Uri = 'doc1.xml';
var docPool = {};
xmlResource1.then(doc => { docPool[url.pathToFileURL(path.resolve('.', doc1Uri))] = doc; })
.then(() => {
//console.log(docPool);
SaxonJS.transform({ documentPool: docPool, stylesheetLocation: 'documentPoolTest1.xsl.sef.json' }, 'async').then(result => {
console.log(SaxonJS.serialize(result.principalResult));
});
});
When I run that with Node I get e.g.
<?xml version="1.0" encoding="UTF-8"?><root>foo</root>
so the doc1.xml
is resolved from my passed in documentPool
property.
However, when I do the same with an XSLT stylesheet having a function exposing xsl:evaluate
with a public function and then call that function with SaxonJS.transform
to pass in e.g. a string doing doc("doc1.xml")
it seems the documentPool is not taken into account and instead I get an error that the file doc1.xml
is not found:
Transformation failure: Error FODC0002 at exposeXslEvaluate.xsl#17
Cannot read file file:///C:/Users/marti/OneDrive/Documents/xslt/saxonjs-documentPool/doc1.xml - ENOENT: no such file or directory, open 'C:\Users\marti\OneDrive\Documents\xslt\saxonjs-documentPool\doc1.xml'
node:internal/process/promises:288
triggerUncaughtException(err, true /* fromPromise */);
^
Error
at new L (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4109:549)
at Object.readFile (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4713:171)
at Object.readFile (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4108:74)
at C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4340:316
at a (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4338:294)
at Object.Ec (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4340:280)
at doc (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4513:298)
at C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4632:444
at Object.I [as evaluate] (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4600:203)
at Object.evaluateXDM (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4933:464)
at C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4630:290
at Object.push (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4390:245)
at e (C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:4987:320)
at C:\Users\marti\AppData\Roaming\npm\node_modules\saxon-js\SaxonJS2N.js:5015:342 {
message: "Cannot read file file:///C:/Users/marti/OneDrive/Documents/xslt/saxonjs-documentPool/doc1.xml - ENOENT: no such file or directory, open 'C:\\Users\\marti\\OneDrive\\Documents\\xslt\\saxonjs-documentPool\\doc1.xml'",
name: 'XError',
code: 'FODC0002',
xsltLineNr: '17',
xsltModule: 'exposeXslEvaluate.xsl'
}
A sample XSLT is e.g.
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:mf="http://example.com/mf"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all">
<xsl:function name="mf:eval" as="item()*" visibility="public">
<xsl:param name="expression" as="xs:string"/>
<xsl:param name="context-item" as="item()?"/>
<xsl:param name="params" as="map(xs:QName, item()*)"/>
<xsl:evaluate xpath="$expression" context-item="$context-item" with-params="$params"/>
</xsl:function>
<xsl:function name="mf:eval" as="item()*" visibility="public">
<xsl:param name="expression" as="xs:string"/>
<xsl:evaluate xpath="$expression" context-item="()" with-params="map{}"/>
</xsl:function>
<xsl:function name="mf:eval" as="item()*" visibility="public">
<xsl:param name="expression" as="xs:string"/>
<xsl:param name="context-item" as="item()?"/>
<xsl:evaluate xpath="$expression" context-item="$context-item" with-params="map{}"/>
</xsl:function>
</xsl:stylesheet>
the Javascript code is e.g.
const path = require('path');
const url = require('url');
const SaxonJS = require('saxon-js');
var xmlResource1 = SaxonJS.getResource({'type': 'xml', 'text' : '<root>foo</root>' });
var doc1Uri = 'doc1.xml';
var docPool = {};
xmlResource1.then(doc => { docPool[url.pathToFileURL(path.resolve('.', doc1Uri))] = doc; })
.then(() => {
console.log(docPool);
SaxonJS.transform({
documentPool: docPool,
stylesheetLocation: 'exposeXslEvaluate.xsl.sef.json',
stylesheetBaseURI: url.pathToFileURL(path.resolve('.', 'exposeXslEvaluate.xsl')),
initialFunction: 'Q{http://example.com/mf}eval',
functionParams: ['doc("doc1.xml")'],
destination: 'raw'
}, 'async').then(result => {
console.log(SaxonJS.serialize(result.principalResult));
});
});
Files
Updated by Martin Honnen over 1 year ago
Just for comparison, SaxonJ (which has a completely different API) does allow me to set up a ResourceResolver (comparable to a documentPool) that is then used for direct XSLT evaluation as well as indirect XPath evaluation in a stylesheet exposing xsl:evaluate
as a function to be called with callFunction
: https://github.com/martin-honnen/SaxonResourceResolverForXslEvaluateTest1
Updated by Norm Tovey-Walsh over 1 year ago
Hi Martin,
Sorry for the delay in responding to this question, and thank you for opening an issue for it. Debbie and I chatted about it this morning and ... it's complicated.
I think you're correct that there's a bug here. The set of available documents in the dynamic context is not passed to fn:transform
or xsl:evaluate
and there's no way for you to make that happen. It doesn't appear to be accidental, but the restriction isn't documented and I think it's reasonable for you to expect that they will be passed through. (The spec gives explicit wiggle room here, see 10.4.2 for example.)
It appears, for better or worse, that our test drivers take advantage of this fact. Because the default is not to pass the available documents through, the test driver can setup the context differently for each test.
My sketch for resolving this is:
- Change the default behavior so that the avialable documents are passed through by default
- Add and document a (vendor extension) option that allows the user to control this behavior. If you don't want the available documents to be passed through, you should be able to prevent it.
Of course there's a question of backwards compatibility here. It's hard to tell if anyone is currently relying on the default behavior. So it might be better to leave the default as it is and provide an option to pass things through. But I'll leave that decision for another day.
Please register to edit this issue
Also available in: Atom PDF Tracking page