Support #6101
closedxsl:import pathing
100%
Description
Hello
I'm running some Node on AWS Lambda, but struggling to get xsl:imports to work there.
My sources are both strings, with the XSL being XSL rather than compiled SEF.
This code works locally, when supplying a (Windows) path as TEMP_DIR.
const transformXml = (xml, xsl) => {
try {
const env = SaxonJS.getPlatform();
const xslDoc = env.parseXmlFromString(xsl);
// hack: avoid error "Required cardinality of value of parameter $static-base-uri is exactly one; supplied value is empty"
xslDoc._saxonBaseUri = `file://${TEMP_DIR}`;
const sef = SaxonJS.compile(xslDoc);
const result = SaxonJS.transform({
stylesheetInternal: sef,
sourceText: xml,
logLevel: 1,
});
return SaxonJS.serialize(result.principalResult, { method: "xml" });
} catch (error) {
console.error(`Transform err: ${error.message}`);
}
};
However, on Lambda, when the files that are xsl:import[ed] sit in /tmp, I get a "can't load file" error.
Where am I going wrong? Is SaxonJS.getResource a better approach? If so, could you point me to an example - I'm struggling to get something based on your docs working.
Regards John
Updated by Martin Honnen over 1 year ago
It seems const sef = SaxonJS.compile(xslDoc);
is not part of the publicly documented API. Do you get the error on the compile
call on Node.js? Or later, on the attempt to transform
? I would try to check/infer whether the compile
call takes an argument or option, similar to the command line, to declare -relocate:on
. That might help to have the files found.
Updated by Martin Honnen over 1 year ago
You haven't shown in all detail how your base stylesheet URI looks for the /tmp
directory, I would think it needs to be e.g. file:/tmp/
.
What should be possible is to use SaxonJS.XPath.evaluate
to call fn:transform
:
'use strict';
const SaxonJS = require('saxon-js');
const transformXml = (xml, xslt, xsltBaseUri) => {
try {
const result = SaxonJS.XPath.evaluate(`
transform(
map {
'stylesheet-text' : $xslt,
'stylesheet-base-uri' : $xsltBaseUri,
'source-node' : parse-xml($xml),
'delivery-format' : 'raw'
}
)
`,
[],
{
params: {
xml: xml,
xslt: xslt,
xsltBaseUri: xsltBaseUri
}
}
);
return SaxonJS.serialize(result.output, { method: "xml" });
} catch (error) {
console.error(`Transform err: ${error.message}`);
}
};
const xml = `<root>
<item>b</item>
<item>c</item>
<item>a</item>
</root>`;
const xslt = `<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:import href="module-test1.xsl"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:copy>
<xsl:apply-templates/>
<xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>`;
console.log(transformXml(xml, xslt, `file:///C:/Users/username/AppData/Local/Temp/`));
Updated by John Cotton over 1 year ago
Thanks for replying Martin.
No, there seems no public documentation on that, but I found it somewhere and it certainly works.
Updated by Martin Honnen over 1 year ago
If I use the following adaption of your code with the undocumented API I can run it successfully under both Windows and Linux:
'use strict';
const SaxonJS = require('saxon-js');
const url = require('url');
const transformXml = (xml, xslt, xsltBaseUri) => {
try {
const env = SaxonJS.getPlatform();
const xslDoc = env.parseXmlFromString(xslt);
xslDoc._saxonBaseUri = xsltBaseUri;
const sef = SaxonJS.compile(xslDoc);
const result = SaxonJS.transform({
stylesheetInternal: sef,
sourceText: xml,
logLevel: 1,
});
return SaxonJS.serialize(result.principalResult, { method: "xml" });
} catch (error) {
console.error(`Transform err: ${error.message}`);
}
};
const xml = `<root>
<item>b</item>
<item>c</item>
<item>a</item>
</root>`;
const xslt = `<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:import href="module-test1.xsl"/>
<xsl:mode on-no-match="shallow-copy"/>
<xsl:output indent="yes"/>
<xsl:template match="/" name="xsl:initial-template">
<xsl:copy>
<xsl:apply-templates/>
<xsl:comment>Run with {system-property('xsl:product-name')} {system-property('xsl:product-version')} {system-property('Q{http://saxon.sf.net/}platform')}</xsl:comment>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>`;
console.log(process.env.TEMP)
const temp_dir = process.env.TEMP;
var temp_dir_url = url.pathToFileURL(temp_dir).href;
if (!temp_dir_url.endsWith('/')) {
temp_dir_url += '/';
}
console.log(transformXml(xml, xslt, temp_dir_url));
It assumes an environment variable TEMP is set to a directory where the module-test1.xsl
is found. Somehow WSL Ubuntu Linux or perhaps Ubuntu Linux in general does not have a predefined TMP o TEMP environment variable.
Updated by John Cotton over 1 year ago
Really appreciate the code, Martin, thank you.
However, the result is the same.
2023-06-28T10:21:47.338+01:00 2023-06-28T09:21:47.338Z a6e0ec33-4751-4e2d-9850-8e487da51dec INFO baseURI set to: file:///tmp
2023-06-28T10:21:47.338+01:00 2023-06-28T09:21:47.338Z a6e0ec33-4751-4e2d-9850-8e487da51dec INFO fs.existsSync("/tmp/landing-pages.xsl") true
2023-06-28T10:21:47.692+01:00 2023-06-28T09:21:47.692Z a6e0ec33-4751-4e2d-9850-8e487da51dec ERROR Transform err: xsl:import of file:///landing-pages.xsl failed:Cannot read file file:///landing-pages.xsl - ENOENT: no such file or directory, open '/landing-pages.xsl'
Updated by John Cotton over 1 year ago
Seemingly, it's because the Transform is not looking in the correct place. Question is: how do I get it to do that?
Updated by John Cotton over 1 year ago
const temp_dir = process.env.TEMP;
..is exactly what I have, although no trailing backslash.
But, even with one, the result is the same.
2023-06-28T10:28:02.887+01:00 2023-06-28T09:28:02.887Z bcdf61bd-14b3-468b-b26f-1c3fd3e58652 INFO baseURI set to: file:///tmp/
2023-06-28T10:28:02.887+01:00 2023-06-28T09:28:02.887Z bcdf61bd-14b3-468b-b26f-1c3fd3e58652 INFO fs.existsSync("/tmp//landing-pages.xsl") true
2023-06-28T10:28:03.231+01:00 2023-06-28T09:28:03.231Z bcdf61bd-14b3-468b-b26f-1c3fd3e58652 ERROR Transform err: xsl:import of file:///landing-pages.xsl failed:Cannot read file file:///landing-pages.xsl - ENOENT: no such file or directory, open '/landing-pages.xsl'
Updated by John Cotton over 1 year ago
FYI, this works:
<xsl:import href="/tmp/landing-pages.xsl" />
But, I don't want to include the path since the same stylesheet is used in another application.
Updated by Martin Honnen over 1 year ago
Yes, I am sorry, I don't know how to use that API, as I commented, I guess the -relocate:on
setting from the command line somehow needs to be set from the API but I don't know where/how and the minimized source code of the released module is hard to read.
Wait whether someone from Saxonica tells you whether and how you can use that API for your use case.
Updated by John Cotton over 1 year ago
Thanks Martin. I appreciate your replies.
Updated by Norm Tovey-Walsh over 1 year ago
Wait whether someone from Saxonica tells you whether and how you can
use that API for your use case.
I’m afraid that API is only accidentally visible. We have plans to make
that an official API, but it wasn’t supposed to be visible yet. It’s
undocumented because the design is unfinished.
You should compile SEF files in advance or use the fn:transform function
for the time being.
Sorry about that.
Be seeing you,
norm
--
Norm Tovey-Walsh
Saxonica
Updated by John Cotton over 1 year ago
Thanks to both.
I finally got it working with Martin's
const result = SaxonJS.XPath.evaluate(` ....
It seems the trailing slash on the BaseUri is critical. With it, it works. Without it, it fails and (slightly oddly) the path doesn't get reported in the error (ie the unfound file:///tmpimported.xsl gets reported as file:///imported.xsl
).
Updated by John Cotton over 1 year ago
John Cotton wrote in #note-12:
Thanks to both.
I finally got it working with Martin's
const result = SaxonJS.XPath.evaluate(` ....
It seems the trailing slash on the BaseUri is critical. With it, it works. Without it, it fails and (slightly oddly) the path doesn't get reported in the error (ie the unfound
file:///tmpimported.xsl
gets reported asfile:///imported.xsl
).
Updated by Martin Honnen over 1 year ago
John Cotton wrote in #note-12:
Thanks to both.
I finally got it working with Martin's
const result = SaxonJS.XPath.evaluate(` ....
It seems the trailing slash on the BaseUri is critical. With it, it works. Without it, it fails and (slightly oddly) the path doesn't get reported in the error (ie the unfound file:///tmpimported.xsl gets reported as
file:///imported.xsl
).
You can try resolve-uri('imported.xsl', 'file:///tmp')
with other XPath 2/XSLT 2 or later or XQuery implementations like BaseX, the result is indeed file:///imported.xsl
. So file URI resolution if you have a file URI denoting a folder or directory as the base URI is only ending up with a URI in that directory if the base URI ends in a slash.
Updated by Norm Tovey-Walsh about 1 year ago
- Status changed from New to Resolved
- Applies to JS Branch 2, Trunk added
- Fix Committed on JS Branch 2, Trunk added
I've removed the compile
method from the public API. When the design is complete and we're ready to document it, we can put it back.
Updated by Debbie Lockett about 1 year ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in JS Release set to SaxonJS 2.6
Bug fix applied in the SaxonJS 2.6 maintenance release.
Please register to edit this issue
Also available in: Atom PDF Tracking page