Bug #4690
closedgenerate-id problem when chaining stylesheets and storing intermediary results as a tree
100%
Description
While trying to run Schematron with Saxon-JS 2 instead of Saxon Java I have run into an odd problem: the schematron compilation consists of three XSLT steps to convert a Schematron schema to an XSLT stylesheet; when I run these steps with Saxon-JS 2 SaxonJS.transform
using a tree as the intermediary result format the third step doing the compilation fails with an error by Saxon-JS that a required parameter has not been supplied.
On inspecting the stylesheet it seems the parameter is passed on using generate-id()
but somehow that doesn't bind a value in the receiving template.
This only happens when I use a tree as the intermediary result, if I serialize intermediary results the problem doesn't occur.
I have managed to reduce the problem to a two step process where the first does an identity spelled out as a template:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="2.0">
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and the second XSLT tries to pass on a generate-id()
inside of for-each-group
:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="/*">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:for-each-group select="item" group-by=".">
<xsl:apply-templates select=".">
<xsl:with-param name="id" as="xs:string" select="generate-id()"/>
</xsl:apply-templates>
</xsl:for-each-group>
</xsl:copy>
</xsl:template>
<xsl:template match="item">
<xsl:param name="id" as="xs:string" required="yes"/>
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="generated-id" select="$id"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
When I run the two stylesheets with Node.js using code
require('saxon-js');
const step1Result = SaxonJS.transform({
stylesheetFileName : 'identity-2.sef.json',
sourceFileName : 'sample1.xml'
});
const step2Result = SaxonJS.transform({
stylesheetFileName : 'generate-id-test2.sef.json',
sourceNode : step1Result.principalResult,
destination : 'serialized'
});
console.log(step2Result.principalResult);
I get an error
message: 'Required parameter $Q{}id not supplied',
code: 'XTDE0700',
xsltLineNr: '21',
xsltModule: 'generate-id-test2.xsl',
A sample input would be
<?xml version="1.0" encoding="UTF-8"?>
<root>
<item>a</item>
<item>b</item>
</root>
If I run the second stylesheet alone all works fine, somehow the first step, although doing nothing but an identity transformation, causes the problem with the binding of generate-id()
in the second step.
The used sef.json
files were simply generated using xslt3
from above shown XSLT stylesheets.
Updated by Michael Kay almost 4 years ago
- Status changed from New to In Progress
- Assignee changed from Debbie Lockett to Michael Kay
- Priority changed from Low to Normal
I've attempted without success to reproduce this by running the two stylesheets from a third stylesheet using fn:transform(). It seems the two transformations really do need to be separate calls on SaxonJS.transform(). Unfortunately this makes debugging a whole lot more difficult...
I have now reproduced the problem within the API testing framework.
Updated by Michael Kay almost 4 years ago
It seems that the principal result of the first transformation is an element node (named root
), not a document node. This means that in the second transformation, the item
template is being called directly ( the match="/*"
template does not fire because this pattern requires the element to have a document node as its parent.)
So, is this correct?
I think not. The API specification for SaxonJS.transform()
says that the default for destination
is application
, and that in the absence of a build-tree
option, this is equivalent to document
, which should cause the result to be wrapped in a document node.
The actual logic (transform.js#407) is
} else if (["raw", "document", "application"].includes(destination)) {
let buildTree = destination === "document";
if (destination === "application") {
if (typeof outputProps["build-tree"] !== "undefined") {
buildTree = outputProps["build-tree"];
}
}
which with these default options leads to buildTree = false. We should add
} else if (["raw", "document", "application"].includes(destination)) {
let buildTree = destination === "document";
if (destination === "application") {
if (typeof outputProps["build-tree"] !== "undefined") {
buildTree = outputProps["build-tree"];
} else {
buildTree = true;
}
}
to make it match the documentation.
Tried this; the test case now works.
Updated by Michael Kay almost 4 years ago
Unfortunately quite a few of the Node.js API tests are written to expect an element node to be returned. For example sync-trans-001 has
expect(result.principalResult.nodeType).to.equal(1);
(NodeType=1 is an element node, NodeType=9 is a document node)
But I think the documentation is clear, and users will expect a document node to be returned in this scenario, so I think we should apply the change and fix the tests to expect NodeType=9.
Updated by Michael Kay almost 4 years ago
With the patch applied, these tests are actually returning a DOCUMENT_FRAGMENT node (11) rather than a DOCUMENT node (9). Of course, these are two different DOM representations of an XDM document node. I think returning a document fragment is correct, because there's no guarantee that the result document will have a single element root, but the documentation should perhaps be clarified.
Updated by Community Admin almost 4 years ago
- Applies to JS Branch 2 added
- Applies to JS Branch deleted (
2.0)
Updated by Norm Tovey-Walsh almost 4 years ago
- Status changed from In Progress to Resolved
Updated by Debbie Lockett almost 4 years ago
- Status changed from Resolved to Closed
- % Done changed from 0 to 100
- Fixed in JS Release set to Saxon-JS 2.1
Bug fix applied in the Saxon-JS 2.1 maintenance release.
Please register to edit this issue
Also available in: Atom PDF Tracking page