XSLT: need alternative to document()-function for multi-source processing - xslt

I'm adapting an XSLT from a third party which transforms an arbitrary number of XMLs into a single HTML document. It's a pretty complex script and it will be revised in the future, so I'm trying to do a minimal adaptation in order to get it to work for our needs.
The following is a stripped down version of the XSLT (containing the essentials):
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml">
<xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes"/>
<xsl:param name="files" select="document('files.xml')//File"/>
<xsl:param name="root" select="document($files)"/>
<xsl:template match="/">
<xsl:for-each select="$root/RootNode">
<xsl:apply-templates select="."/>
</xsl:for-each>
</xsl:template>
<xsl:template match="RootNode">
<xsl:for-each select="//Node">
<xsl:text>Node: </xsl:text><xsl:value-of select="."/><xsl:text>, </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Now files.xml contains a list of all the URLs of the files to be included (in this case the local files file1.xml and file2.xml). Because we want to read XMLs from memory rather than from disk, and because the invocation of the XSLT only allows for a single XML source, I have combined the two files in a single XML document. The following is a combination of two files (there may be more in a real situation)
<?xml version="1.0" encoding="UTF-8"?>
<TempNode>
<RootNode>
<Node>1</Node>
<Node>2</Node>
</RootNode>
<RootNode>
<Node>3</Node>
<Node>4</Node>
</RootNode>
</TempNode>
where the first RootNode originally resided in file1.xml and the second in file2.xml.
Due to the complexity of the actual XSLT, I've figured that my best shot is to try to alter the $root-param. I've tried the following:
<xsl:param name="root" select="/TempNode"/>
The problem is this. In the case of <xsl:param name="root" select="document($files)"/>, the XPath expression "//Node" in <xsl:for-each select="//Node"> selects the Node's from file1.xml and file2.xml independently, i.e. producing the following (desired) list:
Node: 1, Node: 2, Node: 3, Node: 4,
However, when I combine the content of the two files into a single XML and parse this (and use the suggested $root-definition), the expression "//Node" will select all Node's that are children of the TempNode. (In other words, the desired list, as represented above, is produced twice due to the combination with the outer <xsl:for-each select="$root/RootNode"> loop.)
(A side note: as observed in comment a) in this page, document() apparently changes the root node, perhaps explaining this behavior.)
My question becomes:
How can I re-define $root, using the combined XML as source instead of a multi-source through document(), so that the list is only produced once, without touching the remainder of the XSLT? It's like if $root defined using the document()-function, there is no common root node in the param. Is it possible to define a param with two "separate" node trees?
Btw: I've tried defining a document like this
<xsl:param name="root">
<xsl:for-each select="/TempNode/*">
<xsl:document>
<xsl:copy-of select="."/>
</xsl:document>
</xsl:for-each>
</xsl:param>
thinking it might solve the problem, but the "//Node" expression still fetches all the Nodes. Is the context node in the <xsl:template match="RootNode">-template actually somewhere in the input document and not the param? (Honestly, I'm pretty confused when it comes to context nodes.)
Thanks in advance!

(Updated more)
OK, some of the problem is becoming clear. First, just to make sure I understand, you aren't actually passing parameters for $files and $root to the XSLT processor invocation, right? (They might as well be variables rather than params?)
Now to the main issues... In XPath, when you evaluate an expression that begins with "/" (including "//"), the context node is ignored [mostly]. Therefore, when you have
<xsl:template match="RootNode">
<xsl:for-each select="//Node">
the matched RootNode is ignored. Maybe you wanted
<xsl:template match="RootNode">
<xsl:for-each select=".//Node">
in which the for-each would select Node elements that are descendants of the matched RootNode? This would fix your problem of generating the desired node list twice.
I inserted [mostly] above because I recalled that an "absolute location path" starts from "the root node of the document containing the context node". So the context node does affect what document is used for "//Node". Maybe that's what you intended all along? I guess I was slow to catch on to that.
(A side note: as observed in comment
a) in this page, document() apparently
changes the root node, perhaps
explaining this behavior.)
Or more precisely,
An absolute location path ["/..."]
followed by a relative location
path... selects the set of nodes that
would be selected by the relative
location path relative to the root
node of the document containing the
context node.
document() doesn't actually change anything, in the sense of side effects; rather, it returns a set of nodes contained (usually) by different documents than the primary source document. XSLT instructions like xsl:apply-templates and xsl:for-each establish new values for the context node inside the scope of their template bodies. So if you use xsl:apply-templates and xsl:for-each with select="document(...)/...", the context node inside the scope of those instructions will belong to an external document, so any use of "/..." as an XPath will start from that external document.
Updated again
How can I re-define $root, using the
combined XML as source instead of a
multi-source through document(), so
that the list is only produced once,
without touching the remainder of the
XSLT?
As #Alej hinted, it's really not possible given the above constraint. If you're selecting "//Node" in each iteration of the loop over "$root/RootNode", then in order for each iteration not to select the same nodes as the other iterations, each value of "$root/RootNode" must be in a different document. Since you're using the combined XML source, instead of a multi-source, this is not possible.
But if you don't insist that your <xsl:for-each select="//..."> XPath expression cannot change, it becomes very easy. :-) Just put a "." before the "//".
It's like if $root defined using the document()-function, there is no common root node
in the param.
The value of the param is a node-set. All nodes in the set may be contained in the same document, or they may not, depending on whether the first argument to document() is a nodeset or just a single node.
Is it possible to define a param with two "separate" node trees?
I believe by "separate", you mean "belonging to different documents"? Yes it is, but I don't think you can do it in XSLT 1.0 unless you're selecting nodes that belong to different documents in the first place.
You mentioned trying
<xsl:param name="root">
<xsl:for-each select="/TempNode/*">
<xsl:document>
<xsl:copy-of select="."/>
</xsl:document>
</xsl:for-each>
</xsl:param>
but <xsl:document> is not defined in XSLT 1.0, and your stylesheet says version="1.0". Do you have XSLT 2.0 available? If so, let us know and we can pursue this option. To be honest, <xsl:document> is not familiar territory for me. But I'm happy to learn along with you.

You can apply only nodes you need:
Input:
<?xml version="1.0" encoding="UTF-8"?>
<TempNode>
<RootNode>
<Node>1</Node>
<Node>2</Node>
</RootNode>
<RootNode>
<Node>3</Node>
<Node>4</Node>
</RootNode>
</TempNode>
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="html" indent="yes"/>
<xsl:template match="/">
<xsl:copy>
<xsl:apply-templates select="TempNode/RootNode"/>
</xsl:copy>
</xsl:template>
<xsl:template match="RootNode">
<xsl:value-of select="concat('RootNode-', generate-id(.), '
')"/>
<xsl:apply-templates select="Node"/>
</xsl:template>
<xsl:template match="Node">
<xsl:value-of select="concat('Node', ., '
')"/>
</xsl:template>
</xsl:stylesheet>
Output:
RootNode-N65540
Node1
Node2
RootNode-N65549
Node3
Node4

Related

XPath filter not working on XSL

I have the following XML
<?xml version="1.0"?>
<people><human><sex>male</sex><naxme>Juanito</naxme>
</human>
<human><sex>female</sex><naxme>Petra</naxme></human>
<human><sex>male</sex><naxme>Anaximandro</naxme></human>
</people>
and this XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="no"/>
<xsl:template match="/people/human[sex='male']">
<xsl:value-of select="naxme"/>
</xsl:template>
</xsl:stylesheet>
I'm expecting it to filter out the female, and it kind of works but I get odd values for the non-matching nodes:
Juanito
femalePetra
Anaximandro
I'm expecting the same output as with
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" indent="no"/>
<xsl:template match="/people/human">
<xsl:if test = "sex='male'">
<xsl:value-of select="naxme"/>
</xsl:if> </xsl:template>
</xsl:stylesheet>
Thanks!
I'll expand on Daniels answer which covers a little of the why.
The reason you are getting two different outputs comes down to the built-in template rules, and how nodes and text are treated by default.
Summarising that link, if no other template exists, there are default templates that ensure every node - be it element, text, attribute, comment - will be encoutered, just to ensure that other nodes that do have rules can be processed correctly.
With this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/people/human[sex='male']">
<xsl:value-of select="naxme"/>
</xsl:template>
</xsl:stylesheet>
you have an explicit rule that says:
If you find a node that matches the XPath /people/human[sex='male] do this template.
Along with the default rule:
Find all nodes, and then process all of their children. If it is text, just output the text.
This default rule is why your template is being processed, since you have no explicit rule for the root node - / - it any every child and grandchild node are processed using the default rules, unless another exists. As such, each node is traversed using the defaults, except for the nodes matching /people/human[sex='male]. The result of this is that when you have a node that is "female" the text is being spat out instead of ignored.
However, contrast this with:
<xsl:template match="/people/human">
<xsl:if test = "sex='male'">
<xsl:value-of select="naxme"/>
</xsl:if>
</xsl:template>
Where the rule becomes:
If you find a node that matches the XPath /people/human do this template.
It just so happens that in that template, you have an extra condition that says, if it is male, then process it in some way, with no other conditions, so if a "female" node is encountered it is now blank in the output.
Lastly, the reason why Daniels answer works but could easily break, is that it changes the rule for processing text. Instead of now copying all text as in the default rules, it outputs nothing (as per the empty template. However, if you had any other templates which used xsl:apply-templates to process text and were expecting text, they would also now output nothing.
It's probably because of XSLT's built-in template rules. Try adding this template:
<xsl:template match="text()"/>

Xpath is not working on variable having xml string

I am facing issue in writing xapth. Let me explain the problem.
I am writing xslt to transform some xml. The xslt also loads one xml file from disk into xslt variable.
PeopleXml.xml:
<TestXml>
<People>
<Person id="MSA1" name="Sachin">
<Profession>
<Role>Developer</Role>
</Profession>
</Person>
<Person id="ZAG4" name="Rahul">
<Profession>
<Role>Tester</Role>
</Profession>
</Person>
</People>
</TestXml>
XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://MyNamespace"
version="2.0">
<xsl:variable name="PeopleXml" select ="document('PeopleXml.xml')"/>
<xsl:variable name="peopleList" select="$PeopleXml/TestXml/People/Person"/>
<xsl:variable name="person1" select="MSA1"/>
<xsl:variable name="person" select="$peopleList/Person[#id=$person1]/#name"/>
<xsl:template match="/">
<xsl:value-of select="$person"/>
</xsl:template>
</xsl:stylesheet>
Issue: The xpath "$peopleList/Person[#id=$person1]/#name" is not returning anything. Infact, $peopleList/Person also does not work. However, I can see two person nodes in $peopleList variable when I debugged the code.
Could anyone help me, what I am doing wrong in xpath?
EDIT
Above xapth issue has been resolved after applying Daniel's solution. Now, only issue remained is with accessing child nodes of person based on some condition.
Following test does not work.
<xsl:variable name="roleDev" select="'Developer'"/>
<xsl:when test="$peopleList/Profession/Role=$roleDev">
<xsl:value-of select="We have atleast one Developer"/>
</xsl:when>
Your problem is here:
<xsl:variable name="person1" select="MSA1"/>
This results in having the $person1 variable empty.
Why?
Because the expression MSA1 is evaluated -- the current node doesn't have any children named "MSA1" and nothing is selected.
Solution:
Specify the wanted string as string literal:
<xsl:variable name="person1" select="'MSA1'"/>
Your Second Question:
Now, only issue remained is with accessing child nodes of person based
on some condition.
Use:
boolean($peopleList[Profession/Role = 'Developer'])
This produces true() exactly when there is a node in $peopleList such that it has at least one Profession/Role crand-child whose string value is the string "Developer"
Since the variable peopleList is already Person nodes, you should access them like this:
<xsl:variable name="person" select="$peopleList[#id=$person1]/#name"/>

How to "genericize" an XML-to-XML XSL transform to work with files having different tags but same basic structure

I have a set of XML files similar to this:
Example Input File:
<a-list>
<a-item key_field="unique1" other="foo"/>
<a-item key_field="unique2" other="foo"/>
...
</a-list>
and I have a transform that will merge these files into a single file of the same structure. It generates the same outer level element (<a-list>) containing all of the <a-item> elements from the files, and discards duplicate entities (entities having the same key_field). I am now faced with several other sets of files, each very similar in its top level structure, that I need to perform almost the same exact kind of merge.
For example, these files might look like this:
<b-list>
<b-item different_key_field="unique93" other="foo"/>
<b-item different_key_field="unique94" other="foo"/>
...
</b-list>
These other files have different data in them, but the transform is generically the same: I need to take multiple input files that have the same structure (a root level containing a list of items, where the items are uniquely identified by a key attribute), and produce an output file that has the top level element containing all of the (unique) items from each of the input files.
Unfortunately, my "merge" transform operates specifically on the named elements in the input files (e.g. it has templates for <a-item> nodes, and it specifically looks for duplicated key_field attributes). It is fairly trivial to copy the transform, search/replace the element names and the key attribute name, but it seems horribly inefficient to duplicate the same code once for each type of input file. The only things that are different are the name of the root element, the name of the item element, and the name of the key attribute. What I want to do (merge) remains the same.
How can I write a transformation that can perform this kind of merge operation without specifying the exact names of the elements/attribute, so that I can invoke the same transform for each type of file? Is there any way to accept the root element name, item element name, and key attribute name as parameters?
As additional constraints, I'm limited to using Xalan, so I believe that means XSL 1.0 only, and it would be best to avoid any extensions.
I thought about transforming an XSL with another XSL, but that seems rather convoluted for such a simple thing.
I've tried searching here and around the internet via Google, but I'm somewhat new to XSL, and all of the words I can think to search in association with XSL or XSLT seem to have specific meanings in the XSL context that make them less useful as search terms (e.g. XSL template, generic XSL, etc.). Some vocab and some good links would be great, an example would be amazing.
Many Thanks,
Russ
Here is a complete solution:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="pDoc1File" select="'b-list-file1.xml'"/>
<xsl:param name="pDoc2File" select="'b-list-file2.xml'"/>
<xsl:param name="pElemName" select="'b-item'"/>
<xsl:param name="pKeyAttrName" select="'different_key_field'"/>
<xsl:variable name="vDoc1" select="document($pDoc1File)"/>
<xsl:variable name="vDoc2" select="document($pDoc2File)"/>
<xsl:template match="/">
<xsl:apply-templates select="$vDoc1/node()"/>
</xsl:template>
<xsl:template match="/*">
<xsl:copy>
<xsl:copy-of select="#*"/>
<xsl:copy-of select=
"*[name()=$pElemName]
[not(#*[name()=$pKeyAttrName]
=
$vDoc2/*/*[name()=$pElemName]
/#*[name()=$pKeyAttrName])
]"/>
<xsl:copy-of select=
"$vDoc2/*/*[name()=$pElemName]
[not(#*[name()=$pKeyAttrName]
=
$vDoc1/*/*[name()=$pElemName]
/#*[name()=$pKeyAttrName])
]"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
File: b-list-file1.xml
<b-list>
<b-item different_key_field="unique93" other="foo"/>
<b-item different_key_field="unique94" other="foo"/> ...
</b-list>
File: b-list-file2.xml
<b-list>
<b-item different_key_field="unique92" other="foo"/>
<b-item different_key_field="unique93" other="foo"/> ...
</b-list>
When this transformation is applied on any XML document (not used / ignored), the wanted, correct result is produced:
<b-list>
<b-item different_key_field="unique94" other="foo"/>
<b-item different_key_field="unique92" other="foo"/>
</b-list>
Explanation:
The filepaths (URLs) to the documents to be processed are provided as global (external to the transformation) parameters.
The name of the elements is provided in a global parameter.
The name of the "key" attribute is provided as a global/external parameter.
Finally, the XPath expressions in the select attributes of the two <xsl:copy-of> instructions, use the values of the parameters for selecting the appropriately-named elements and attributes.
Do note: Every XSLT processor has its own implementation of setting and passing parameters to the transformation. Read this in the Xalan documentation.
Well you can solve your problem with parameters. There is no notion of templated xsl like C++ templates for example, but with parameters you can achieve this effect. For example one of my xslt files looks like this :
<xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xml:space="preserve" version="2.0">
<!--Output type to be used with the xsl:result-document-->
<xsl:output name="html" encoding="utf-8" method="html" indent="yes" doctype- system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
<!--Global output type-->
<xsl:output encoding="utf-8" method="html" indent="yes" doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"/>
<!--xml output-->
<xsl:output name="xml" encoding="utf-8" method="xml" indent="yes"/>
<xsl:param name="sapFile" as="xs:string" required="yes"/>
<xsl:param name="automaticDate" as="xs:string" required="yes"/>
<xsl:param name="generatePDF" as="xs:string" required="yes"/>
<xsl:param name="fileName" as="xs:string" required="yes"/>
.
.
.
The important element here is xsl:param. You can pass these parameter when you "call" your xsl transform. These parameters in your case would be for example : in one case a-list and in the other case b-list etc. Then you can use these parameters like $param wherever you want in your xslt code so that you can achieve genericity.
I hope that give you some pointers :)

XSLT apply-template with mode - Incorrect result with no matching mode

Here's a simple case.
Here's my XML:
<?xml version="1.0" encoding="utf-8" ?>
<dogs>
<dog type="Labrador">
<Name>Doggy</Name>
</dog>
<dog type="Batard">
<Name>Unknown</Name>
</dog>
</dogs>
This XML is used with two Xslt. This is the common one:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="text"/>
<xsl:template match="dogs">
<xsl:text>First template
</xsl:text>
<xsl:apply-templates select="." mode="othertemplate" />
</xsl:template>
</xsl:stylesheet>
This is the child one:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:include href="transform.xslt"/>
<xsl:template match="dogs" mode="othertemplate">
<xsl:text> Other template</xsl:text>
</xsl:template>
</xsl:stylesheet>
The child includes the common one (called transform.xslt).
When I execute the child, I get the expected result:
First template
Other template
When I execute the common one, I get this strange results:
First template
Doggy
Unknown
The common one applies a template with the mode "othertemplate". This mode is only included, some times, in the child xslt.
I want that, if there's no template with mode "othertemplate", then nothing should be outputted.
I don't want to include a template with mode "othertemplate" with empty body for all xslt files that does not have to use this template mode...
What should I do?
Thanks
Built-in Template Rules in XSLT
The element contents and the extra whitespace appear because of XSLT's built-in template rules, also known as default templates. These rules are applied when there is no other matching template. The built-in template rules are
<xsl:template match="*|/">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="text()|#*">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="processing-instruction()|comment()"/>
Built-in template rules process root and element nodes recursively and copy text (and attribute values if attribute nodes are selected). The built-in template rule for processing instructions and comments is to do nothing. Namespace nodes are not processed by default. Note that <xsl:apply-templates/> is practically a shorthand for <xsl:apply-templates select="child::node()"/> so it will not select attribute or namespace nodes.
There is also a built-in template rule for every mode. These templates are like default templates for elements and root except they continue processing in the same mode.
<xsl:template match="*|/" mode="foobar">
<xsl:apply-templates mode="foobar"/>
</xsl:template>
Because your stylesheet doesn't have a template matching dogs with mode othertemplate this built-in template rule is applied which in this case results in processing all child nodes and eventually printing the text nodes. Indentation and line feeds between the source document's elements are also text nodes so they also get printed and cause the extra whitespace in the output.
Warning on non-terminating loops because of <xsl:apply-templates select="."/>
Typically apply-templates is used to process descendants. In your example code you selected the current node when calling apply-templates. This may result in non-terminating loop if the template itself is applied because of an apply-templates command inside it. Example below
<xsl:template match="foobar">
<!-- This is an infinite loop -->
<xsl:apply-templates select="."/>
</xsl:template>
By the way. On a general rule on combining stylesheets, think carefully which template you should run and which one you should import or include. (I have read that as a general practice, Michael Kay seems to recommend using <xsl:import> to import the general-case stylesheet into the special-case stylesheet, not the other way round.)
The built-in XSLT templates are defined and selected for every mode. So, the built-in template for text nodes is selected and (by definition) it outputs the text node.
To suppress this, you need to override thie built-in template for text nodes (also possibly for elements) in your desired mode with an empty template:
<xsl:template match="text()" mode="othertemplate"/>
Include the above in your imported stylesheet.

XPath: Selecting nodes included by an <xsl:include>

There's an XSL that includes another XSL:
<xsl:include href="registered.xsl"/>
That included file has a list of nodes:
<g:registered>
<node1/>
<node2/>
</g:registered>
Documentation says that "the children of the <xsl:stylesheet> element in this document replace the element in the including document", so I would think that, given the include directive has worked, I can select g:registered nodes like if they always belonged to the inluding document:
select="document('')/xsi:schema/g:registered"
That returns an empty nodeset though.
However, this:
select="document('registered.xsl')/xsi:schema/g:registered"
does select what is required, but that, as I suppose, means opening the included file for the second time which doesn't seem nice to me.
So how do I select those includes without opening the file second time?
EDIT
Requested document structure:
Included document:
<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:g="http://www.sample.com/ns">
<g:registered-templates>
<SampleTemplate/>
<WrongTemplate/>
</g:registered-templates>
<xsl:include href="Sample Template.xsl" />
<xsl:include href="Wrong Template.xsl" />
</xsl:stylesheet>
Including document:
<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:g="http://www.sample.com/ns">
<xsl:output method="text"/>
<xsl:include href="Label Registration.xsl"/>
<!-- How do I refer to just loaded inclusion without directing engine to the file again? -->
<xsl:variable name="template-names" select="document('Label Registration.xsl')/xsl:stylesheet/g:registered-templates"/>
<xsl:template match="Job">
<xsl:for-each select="WorkItem">
<xsl:apply-templates select="$template-names/*[local-name()=current()/#name]">
<xsl:with-param name="context" select="." />
</xsl:apply-templates>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Selecting into your variable template-names queries the transformation source document - not your included stylesheet.
If you want to refer to g:registered-templates you have to point to the file like a second source document.
EDIT
I'm not really sure. but it looks like you want to create an element according to the attribute value.
In that case this post will be interesting for you.
<xsl:for-each select="WorkItem">
<xsl:element name="{Type}" >
<xsl:value-of select="current()/#name"/>
</xsl:element>
</xsl:for-each>
Ok, my understanding was wrong.
The document('') function opens the file anyway, so it has no advantages, performance-wise, over document('registered.xsl'). And since it queries the file, not the now-modified DOM model of current document, the result does not include my includes.
And it is not possible to query DOM model of the transformation template itself, as far as I'm concerned.