Eric, thank you for your interest in this subject. The issue tracker appears to be inactive since 2011, and it’s not possible to register an account there. Your XSLT files didn’t work for me with an error “An attempt was made to insert a node where it is not permitted”, apparently because they are attempting to create multiple elements “first”, “emitted”, “Output” and “last” at the root level. I added a new wrapping element to the “Root” template and it started working a bit better. For the “TestNotOK.xsl” file, all three XSLT engines (JDK-Xalan, Xalan 2.7.2, Saxon) give the same result:
<OutputRoot xmlns:exslt="http://exslt.org/common" xmlns:str="http://exslt.org/strings"> <first>foo</first> <emitted>foo</emitted> <Output> <Display></Display> <Usage></Usage> <Display></Display> </Output> <emitted>foo</emitted> <last>foo</last> </OutputRoot> The parameter isn’t being passed to the “Created” template. For the “TestOK.xsl” file, Xalan 2.7.2 gives the same result, but JDK-Xalan and Saxon work better: <OutputRoot xmlns:exslt="http://exslt.org/common" xmlns:str="http://exslt.org/strings"> <first>foo</first> <emitted>foo</emitted> <Output> <Display>foo</Display> <Usage></Usage> <Display>foo</Display> </Output> <emitted>foo</emitted> <last>foo</last> </OutputRoot> Here the parameter is being passed successfully. What’s the difference? “TestNotOK” and “TestOK” differ in having the “/” template. Probably there is something to do with calling “apply-templates” on a result-tree-fragment. I have read somewhere that in such cases the result-tree-fragment is being treated as a separate document with its own “document element”, and the templates should be called on that document element first, but, since I don’t have a dedicated template for that element, a “default template” is being called, and at this moment the parameter is being forgotten. Probably the “/” template changes this behavior in some way. But I can’t understand why because your “/” template also ignores the parameter, and anyway it appears identical to the “default template” so I can’t see why it can make any difference. I have modified your “/” template by adding a “prm” parameter to it and passing this parameter to “apply-templates”. Please find attached “TestDocTemplatePrm.xsl”. Now it works! The parameter is being passed in all three engines including Xalan 2.7.2: <OutputRoot xmlns:exslt="http://exslt.org/common" xmlns:str="http://exslt.org/strings"> <first>foo</first> <emitted>foo</emitted> <Output> <Display>foo</Display> <Usage></Usage> <Display>foo</Display> </Output> <emitted>foo</emitted> <last>foo</last> </OutputRoot> So indeed the result-tree-fragment is being processed starting with an imaginary “document element”, and if I supply a suitable template which passes the parameter through, it is being passed to the next “Created” template I actually need. Then I uncommented the “key” stuff and finally got working what I initially needed. I had to make another modification. Apparently at some stage of these nested templates the context is lost and the “key” function can’t find the required key. To restore the context, I have added a DOCUMENT_ROOT variable and wrapped the “key” invocation inside a “for-each”. Also I modified the key and the source XML file so that it could find something meaningful. And it finally worked! Please find attached “TestEverythingWorks.xml” and “TestEverythingWorks.xsl”. Xalan 2.7.2 and Saxon give the correct result: <OutputRoot xmlns:exslt="http://exslt.org/common" xmlns:str="http://exslt.org/strings"> <first>foo</first> <emitted>foo</emitted> <Output> <Display>foo</Display> <Usage>bar</Usage> <Display>foo</Display> </Output> <emitted>foo</emitted> <last>foo</last> </OutputRoot> The element is being found in the key and from it the value “bar” is being read. Hooray! I still don’t know why so many twists are required to pass the parameter. Is it correct anyway? Probably it’s all towards the stringent implementation of the XSLT standard. In the JDK-Xalan the parameter was being passed even in the absence of the “/” template, probably that was a bug which has been fixed since then. On the other hand, the JDK-Xalan was losing the parameter which was an obvious bug; now I see that it has been fixed in Xalan 2.7.2. Thank you! Sergey Bederov Senior Developer Cortona3D www.cortona3D.com<http://www.cortona3d.com/> From: Eric J. Schwarzenbach [mailto:eric.schwarzenb...@wrycan.com] Sent: Saturday, March 11, 2023 11:28 PM To: Bederov, Sergey <bede...@cortona3d.com> Cc: j-users@xalan.apache.org Subject: Re: Xalan bug: key( ) clears variable The issue tracker is here: https://issues.apache.org/jira/projects/XALANJ/issues/XALANJ-2357?filter=allopenissues However, I'm seeing even more weirdness with this xsl. It seems I can make it work by making changes that have no reason to make a difference. I'd made a number of changes to my copy of your xsl, to try to make it work, and to test that certain things were working property and were not the cause of the problem. I started to clean that up in case it might be useful for the bug submission. But some change I made caused the variable to be passed and make <Display>foo</Display> appear in the output. Trying to backtrack I saw the problem come and go unexpectedly. I isolated one change that causes the problem to go away. My attached testOK.xsl works properly. testNotOK.xsl exhibits the problem. The only difference is testNotOK.xsl having a "/" template (which was not even in your version and should not be having any effect). I've also gotten other changes that should be unrelated to cause the problem to come and go. It is as if various changes "stir the pot" and activate or deactivate the problem. I realize this makes no sense and makes me sound like a crazy person, but I urge you to try my two attached versions of the stylesheet. I suspect that issue you had with the key usage using the JDK version was this same "stir the pot" issue and nothing particularly to do with keys, and it probably behaves the same way in both xalan and the JDK version of it.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" xmlns:exslt="http://exslt.org/common"> <!-- <xsl:key name="keyname" match="Foo" use="@id"/>--> <xsl:template match="/"> <xsl:param name="prm"/> <xsl:apply-templates> <xsl:with-param name="prm" select="$prm" /> </xsl:apply-templates> </xsl:template> <xsl:template match="Root"> <xsl:variable name="created"> <Created /> </xsl:variable> <OutputRoot> <first> <xsl:value-of select="@attr" /> </first> <xsl:call-template name="emitVar"> <xsl:with-param name="prm" select="@attr" /> </xsl:call-template> <xsl:variable name="wtf" select="@attr" /> <xsl:apply-templates select="exslt:node-set($created)"> <xsl:with-param name="prm" select="@attr" /> <!-- note that substituting a literal, like 'foo' for @attr makes no difference --> </xsl:apply-templates> <xsl:call-template name="emitVar"> <xsl:with-param name="prm" select="@attr" /> </xsl:call-template> <last> <xsl:value-of select="@attr" /> </last> </OutputRoot> </xsl:template> <xsl:template name="emitVar"> <xsl:param name="prm" select="'default'" /> <emitted> <xsl:value-of select="$prm" /> </emitted> </xsl:template> <xsl:template match="Created"> <xsl:param name="prm" /> <Output> <Display> <xsl:value-of select="$prm" /> </Display> <Usage> <!-- <xsl:value-of select="key('keyname',$prm)"/>--> </Usage> <Display> <xsl:value-of select="$prm" /> </Display> </Output> </xsl:template> </xsl:stylesheet>
TestEverythingWorks.xml
Description: TestEverythingWorks.xml
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" xmlns:exslt="http://exslt.org/common"> <xsl:key name="keyname" match="Root" use="@attr"/> <xsl:variable name="DOCUMENT_ROOT" select="/"/> <xsl:template match="/"> <xsl:param name="prm"/> <xsl:apply-templates> <xsl:with-param name="prm" select="$prm" /> </xsl:apply-templates> </xsl:template> <xsl:template match="Root"> <xsl:variable name="created"> <Created /> </xsl:variable> <OutputRoot> <first> <xsl:value-of select="@attr" /> </first> <xsl:call-template name="emitVar"> <xsl:with-param name="prm" select="@attr" /> </xsl:call-template> <xsl:variable name="wtf" select="@attr" /> <xsl:apply-templates select="exslt:node-set($created)"> <xsl:with-param name="prm" select="@attr" /> <!-- note that substituting a literal, like 'foo' for @attr makes no difference --> </xsl:apply-templates> <xsl:call-template name="emitVar"> <xsl:with-param name="prm" select="@attr" /> </xsl:call-template> <last> <xsl:value-of select="@attr" /> </last> </OutputRoot> </xsl:template> <xsl:template name="emitVar"> <xsl:param name="prm" select="'default'" /> <emitted> <xsl:value-of select="$prm" /> </emitted> </xsl:template> <xsl:template match="Created"> <xsl:param name="prm" /> <Output> <Display> <xsl:value-of select="$prm" /> </Display> <xsl:for-each select="$DOCUMENT_ROOT"> <Usage> <xsl:value-of select="key('keyname',$prm)/@data"/> </Usage> </xsl:for-each> <Display> <xsl:value-of select="$prm" /> </Display> </Output> </xsl:template> </xsl:stylesheet>