XSLT 2.0 External lookup using key() and document() - xslt

I'm pulling what's left of my hair out trying to get a simple external lookup working using Saxon 9.1.0.7.
I have a simple source file dummy.xml:
<something>
<monkey>
<genrecode>AAA</genrecode>
</monkey>
<monkey>
<genrecode>BBB</genrecode>
</monkey>
<monkey>
<genrecode>ZZZ</genrecode>
</monkey>
<monkey>
<genrecode>ZER</genrecode>
</monkey>
</something>
Then the lookup file is GenreSet_124.xml:
<GetGenreMappingObjectsResponse>
<tuple>
<old>
<GenreMapping DepartmentCode="AAA"
DepartmentName="AND - NEWS AND CURRENT AFFAIRS"
Genre="10 - NEWS"/>
</old>
</tuple>
<tuple>
<old>
<GenreMapping DepartmentCode="BBB"
DepartmentName="AND - NEWS AND CURRENT AFFAIRS"
Genre="11 - NEWS"/>
</old>
</tuple>
... lots more
</GetGenreMappingObjectsResponse>
What I'm trying to achieve is simply to get hold of the "Genre" value based on the "DepartmentCode" value.
So my XSL looks like:
...
<!-- Set up the genre lookup key -->
<xsl:key name="genre-lookup" match="GenreMapping" use="#DepartmentCode"/>
<xsl:variable name="lookupDoc" select="document('GenreSet_124.xml')"/>
<xsl:template match="/something">
<stuff>
<xsl:for-each select="monkey">
<Genre>
<xsl:apply-templates select="$lookupDoc">
<xsl:with-param name="curr-label" select="genrecode"/>
</xsl:apply-templates>
</Genre>
</xsl:for-each>
</stuff>
</xsl:template>
<xsl:template match="GetGenreMappingObjectsResponse">
<xsl:param name="curr-genrecode"/>
<xsl:value-of select="key('genre-lookup', $curr-genrecode)/#Genre"/>
</xsl:template>
...
The issue that I have is that I get nothing back. I currently just get
<?xml version="1.0" encoding="UTF-8"?>
<stuff>
<Genre/>
<Genre/>
<Genre/>
<Genre/>
</stuff>
I have moved all the lookup data to be attributes of GenreMapping, previously as child elements of GenreMapping whenever I entered the template match="GetGenreMappingObjectsResponse" it would just print out all text from every GenreMapping (DepartmentCode, DepartmentName, Genre)!
I can't for the life of me figure out what I am doing wrong. Any helpo/suggestions would be greatly appreciated.
PLease find the current actual XSLT listing:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- Define the global parameters -->
<xsl:param name="TransformationID"/>
<xsl:param name="TransformationType"/>
<!-- Specify that XML is the desired output type -->
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<!-- Set up the genre matching capability -->
<xsl:key name="genre-lookup" match="GenreMapping" use="#DepartmentCode"/>
<xsl:variable name="documentPath"><xsl:value-of select="concat('GenreSet_',$TransformationID,'.xml')"/></xsl:variable>
<xsl:variable name="lookupDoc" select="document($documentPath)"/>
<!-- Start the first match on the Root level -->
<xsl:template match="/something">
<stuff>
<xsl:for-each select="monkey">
<Genre>
<xsl:apply-templates select="$lookupDoc/*">
<xsl:with-param name="curr-genrecode" select="string(genrecode)"/>
</xsl:apply-templates>
</Genre>
</xsl:for-each>
</stuff>
</xsl:template >
<xsl:template match="GetGenreMappingObjectsResponse">
<xsl:param name="curr-genrecode"/>
<xsl:value-of select="key('genre-lookup', $curr-genrecode, $lookupDoc)/#Genre"/>
</xsl:template>
</xsl:stylesheet>
The TransformationID is alway 124 (so the correct lookup file is opened. The Type is just a name that I am currently not using but intending to.

In XSLT 2.0 there are two ways you can do what you want:
One is the three-parameter version of the key function. The third parameter lets you specify the root node you want the key to work on (by default it's always the root of the main document):
<xsl:value-of select="key('genre-lookup', $curr-genrecode,$lookupDoc)/#Genre"/>
Another way is to use the key function under the $lookupDoc node:
<xsl:value-of select="$lookupDoc/key('genre-lookup', $curr-genrecode)/#Genre"/>
Both of these methods are documented in the XSLT 2.0 specification on keys, and won't work in XSLT 1.0.
For the sake of completeness, you'd have to rewrite this to not use keys if you're restricted to XSLT 1.0.
<xsl:value-of select="$lookupDoc//GenreMapping[#DepartmentCode = $curr-genrecode]/#Genre"/>
Aha! The problem is the select="$lookupDoc" in your apply-templates call is calling a default template rather than the one you expect, so the parameter is getting lost.
Change it to this:
<xsl:apply-templates select="$lookupDoc/*">
<xsl:with-param name="curr-genrecode" select="string(genrecode)"/>
</xsl:apply-templates>
That will call your template properly and the key should work.
So the final XSLT sheet should look something like this:
<xsl:variable name="lookupDoc" select="document('XMLFile2.xml')"/>
<xsl:key name="genre-lookup" match="GenreMapping" use="#DepartmentCode"/>
<xsl:template match="/something">
<stuff>
<xsl:for-each select="monkey">
<Genre>
<xsl:apply-templates select="$lookupDoc/*">
<xsl:with-param name="curr-genrecode" select="string(genrecode)"/>
</xsl:apply-templates>
</Genre>
</xsl:for-each>
</stuff>
</xsl:template>
<xsl:template match="GetGenreMappingObjectsResponse">
<xsl:param name="curr-genrecode"/>
<xsl:value-of select="key('genre-lookup',$curr-genrecode,$lookupDoc)/#Genre"/>
</xsl:template>

OK, so this is a bit mental and I don't claim to understand it but it works (sounds like a career in software).
The issue I was having is that when I call the apply-templates and pass in the external document as a variable it never matched any templates even ones called "Genremapping".
So I used a wildcard to catch it, also when calling the apply-templates I narrowed down the node set to be the child I was interested in. This was pretty bonkers a I could print out the name of the node and see "GenreMapping" yet it never went into any template I had called "GenreMapping" choosing instead to only ever go to "*" template.
What this means is that my new template match gets called for every single GenreMapping node there is so it may be a little inefficient. What I realised then was all I needed to do was print sometihng out if a predicate matched.
So it looks like this now (no key used whatsoever):
...
<xsl:template match="/something">
<stuff>
<xsl:for-each select="monkey">
<Genre>
<key_value><xsl:value-of select="genrecode"/></key_value>
<xsl:variable name="key_val"><xsl:value-of select="genrecode"/></xsl:variable>
<code>
<xsl:apply-templates select="$lookupDoc/*/*/*/*">
<xsl:with-param name="curr-genrecode" select="string(genrecode)"/>
</xsl:apply-templates>
</code>
</Genre>
</xsl:for-each>
</stuff>
</xsl:template >
<xsl:template match="*">
<xsl:param name="curr-genrecode"/>
<xsl:value-of select=".[#DepartmentCode = $curr-genrecode]/#Genre"/>
</xsl:template>
...
All of which output:
Note, the last key_value correctly doesn't have a code entry as there is no match in the lookup document.
<?xml version="1.0" encoding="UTF-8"?>
<stuff xmlns:xs="http://www.w3.org/2001/XMLSchema">
<Genre>
<key_value>AAA</key_value>
<code>10 - NEWS</code>
</Genre>
<Genre>
<key_value>AAA</key_value>
<code>10 - NEWS</code>
</Genre>
<Genre>
<key_value>BBB</key_value>
<code>11 - NEWS</code>
</Genre>
<Genre>
<key_value>SVVS</key_value>
<code/>
</Genre>
</stuff>
Answer on a postcode. Thanks for the help Welbog.

Related

XSLT replace variable element text

I have an example document that looks like this
<document>
<memo>
<to>Allen</to>
<p>Hello! My name is <bold>Josh</bold></p>
<p>It's nice to meet you <bold>Allen</bold>. I hope that we get to meet up more often.</p>
<from>Josh</from>
<memo>
</document>
and this is my XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:company="http://my.company">
<xsl:output method="html"/>
<xsl:variable name="link" select="company:generate-link()"/>
<xsl:template match="/document/memo">
<h1>To: <xsl:value-of select="to"/></h1>
<xsl:for-each select="p">
<p><xsl:apply-templates select="node()" mode="paragraph"/></p>
</xsl:for-each>
<xsl:if test="from">
<p>From: <strong><xsl:value-of select="from"/></strong></p>
</xsl:if>
<xsl:copy-of select="$link"/>
</xsl:template>
<xsl:template match="bold" mode="paragraph">
<b><xsl:value-of select="."/></b>
</xsl:template>
<xsl:template match="text()" mode="paragraph">
<xsl:value-of select="."/>
</xsl:template>
</xsl:stylesheet>
and the variable link contains the following example node:
When I do a copy-of out the variable link it prints out the node correctly (but obviously without any text). I want to insert it into the document and replace the text using XSLT. For example, the text could be:
View all of <xsl:value-of select="/document/memo/from"/>'s documents.
So the resulting document would look like:
<h1>To: Allen</h1>
<p>Hello! My name is <b>Josh</b></p>
<p>It's nice to meet you <b>Allen</b>. I hope that we get to meet up more often.</p>
<from>Josh</from>
View all of Josh's documents.
I have been searching the internet on how to do this but I couldn't find anything. If anyone could help I would appreciate it a lot!
Thanks,
David.
You haven't said which XSLT processor that is nor have you shown the code of that extension function to allow us to understand what it returns but based on your comment saying it returns a node you can usually process it further with templates so if you use <xsl:apply-templates select="$link"/> and then write a template
<xsl:template match="a[#href]">
<xsl:copy>
<xsl:copy-of select="#*"/>
View all of <xsl:value-of select="$main-doc/document/memo/from"/>'s documents.
</xsl:copy>
</xsl:template>
where you declare a global variable <xsl:variable name="main-doc" select="/"/> you should be able to transform the node returned from your function.

Applying template more than once on an element

Im struggling a bit with the following. Given that items may contain several part and spec pairs, i want to process each pair, or apply the template to the item more than once.
Currently, each item is processed once and I'm missing the second part.
<figure>
<list>
<item>
<part>
<p>74174</p>
</part>
<spec>
<u>a1</u>
</spec>
<part>
<p>75375</p>
</part>
<spec>
<u>a4</u>
</spec>
</item>
</list>
</figure>
Stylesheet:
<xsl:if test="$a = 'abc'">
<xsl:apply-templates mode="pt" select="/figure/list/item" />
</xsl:if>
<xsl:template mode="pt" match="item[./part]">
<xsl:call-template name="ptt">
<xsl:with-param name="p"><xsl:value-of select="part/p"/>
</xsl:with-param>
<xsl:with-param name="pr">
<xsl:if test="spec/u">
<xsl:element name="pr">
<xsl:element name="rpn">
<xsl:value-of select="spec/u"/>
</xsl:element>
<xsl:element name="rtn">Alt</xsl:element>
</xsl:element>
</xsl:if>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
I simplified and cropped the code a bit since it goes on and on and on..
Edit: This next one is generating my new elements based solely on the input params
<xsl:template name="ptt">
<xsl:param name="p"/>
<xsl:param name=u"/>
</xsl:template>
It seems that you are unwilling to show a self-contained and complete example (for your future questions, do not make it quite so hard for people to help you), so I am not sure whether the following is helpful to you.
Assuming the XML input you have shown, the stylesheet below demonstrates a way to call a named template for each pair of spec and part elements, as you requested.
It uses xsl:for-each-group (which is exclusive to XSLT 2.0, but you did not tell which version of XSLT you use) to define groups that start with a part element, resulting in the said pairs. Then, for each group, the named template ptt is invoked with p and u as parameters.
Stylesheet
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="item">
<xsl:for-each-group select="*" group-starting-with="part">
<xsl:call-template name="ptt">
<xsl:with-param name="p" select="current-group()[1]/p"/>
<xsl:with-param name="u" select="current-group()[2]/u"/>
</xsl:call-template>
</xsl:for-each-group>
</xsl:template>
<xsl:template name="ptt">
<xsl:param name="p"/>
<xsl:param name="u"/>
<result>
<part><xsl:value-of select="$p"/></part>
<spec><xsl:value-of select="$u"/></spec>
</result>
</xsl:template>
</xsl:transform>
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<result>
<part>74174</part>
<spec>a1</spec>
</result>
<result>
<part>75375</part>
<spec>a4</spec>
</result>
Well with call-template and a parameter <xsl:with-param name="p"><xsl:value-of select="part/p"/></xsl:with-param> you are making it harder than it needs to be, assuming XSLT 1.0 the <xsl:with-param name="p"><xsl:value-of select="part/p"/></xsl:with-param> fills the parameter p with a text node of the string value of the first element selected by part/p. So at least use simply <xsl:with-param name="p-elements" select="part/p"/>, then the parameter value is a node-set with all p elements.
Better yet, simply use template matching and apply-templates consistently, then you don't have to struggle with call-template and with-param.
Based on your comments you could just use
<xsl:template mode="pt" match="item[part]">
<xsl:apply-templates select="part" mode="pt"/>
</xsl:template>
<xsl:template match="part" mode="pt">
<xsl:variable name="spec" select="following-sibling::*[1][self::spec]"/>
...
</xsl:template>

XSLT: Passing URL querystring as a parameter

I know this is an old question that has been passed around SO several times but I was wondering whether anyone can expand on whether a URL that has a querystring attached to it can be stripped out via XSLT 1.0 and can be used as a parameter for later use of the XSLT transformation.
For example, I have a URL of http://www.mydomain.com/mypage.htm?param1=a&param2=b
via XSLT, I am looking for a result of something along the lines of:
<xsl:param name="param1">a</xsl:param> and <xsl:param name="param2">b</xsl:param>
where both parameter name (param1, param2) and it's value (a, b) has been extracted from the quesrystring to allow me to use $param1 and $param2 later on say in an if condition
e.g. <xsl:if test="$param1 = 'a'> comes out true but if we use <xsl:if test="$param1 = 'b'> comes out false.
I have seen a similar question here: Retrieve page URL params or page URL in XSLT which uses the str-split-to-words template but I have unsuccessfully got it working (possibly due to me implementing it the wrong way) so any working examples of how it can be done in practice would be massively beneficial.
XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns="http://www.w3.org/1999/xhtml" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ext="http://exslt.org/common">
<xsl:import href="http://fxsl.cvs.sourceforge.net/viewvc/fxsl/fxsl-xslt2/f/strSplit-to-Words.xsl"/>
<xsl:output indent="yes" method="html"/>
<xsl:template match="/">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="$pQString"/>
<xsl:with-param name="pDelimiters" select="'?&'"/>
</xsl:call-template>
</xsl:variable>
<xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
</xsl:template>
<xsl:template match="word">
<xsl:value-of select="."/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>
There are a few problems in your code:
<xsl:import href="http://fxsl.cvs.sourceforge.net/viewvc/fxsl/fxsl-xslt2/f/strSplit-to-Words.xsl"/> I doubt that the wanted stylesheet can be imported directly from its SourceForge view page -- especially taking into account, that it itself imports other FXSL stylesheets. The correct way to use FXSL is to download it to the local computer and reference its stylesheets off the file location it resides in at the local computer.
...
.2. <xsl:with-param name="pStr" select="$pQString"/> This will produce a compilation error because you missed to define the $pQString global/external parameter. You need to define this parameter at global level. It can be given a default value (for example a particular URL) for easier testing. However, the idea of using this parameter is that the invoker of the transformation should pass this parameter to the transformation.
.3. The results of the transformation are written to the output. While this is good for demonstration purposes, you want to be able to use these results later in the transformation. The way to do this is to capture these results in a variable, make another variable from it, with a regular tree (from its RTF type) and then reference the nodes of this last variable.
Here is an example of the code you want (provided that you have downloaded FXSL, unzipped the distribution and saved this code in the same directory as the unzipped distribution of FXSL):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common"
>
<xsl:import href="strSplit-to-Words.xsl"/>
<xsl:output indent="yes" omit-xml-declaration="yes"/>
<xsl:param name="pUrl" select=
"'http://www.mydomain.com/mypage.htm?param1=a&param2=b'"/>
<xsl:param name="pQString" select=
"substring-after($pUrl, '?')"
/>
<xsl:template match="/">
<xsl:variable name="vwordNodes">
<xsl:call-template name="str-split-to-words">
<xsl:with-param name="pStr" select="$pQString"/>
<xsl:with-param name="pDelimiters"
select="'?&'"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="vrtfqueryParams">
<xsl:apply-templates select="ext:node-set($vwordNodes)/*"/>
</xsl:variable>
<xsl:variable name="vqueryParams" select="ext:node-set($vrtfqueryParams)/*"/>
<xsl:value-of select="$vqueryParams/#name[. ='param1']"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="$vqueryParams[#name = 'param1']"/>
<xsl:text>
</xsl:text>
<xsl:value-of select="$vqueryParams/#name[. ='param2']"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="$vqueryParams[#name = 'param2']"/>
</xsl:template>
<xsl:template match="word">
<param name="{substring-before(.,'=')}">
<xsl:value-of select="substring-after(.,'=')"/>
</param>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used in this demo), the wanted, correct result -- the query-string parameters referenced of a results variable by name -- is produced:
param1 : a
param2 : b

XSLT: nested for-each and dynamic variable

This is my XML and XSLT code
<root>
<act>
<acts id>123</acts>
</act>
<comp>
<comps id>233</comps>
</comp>
</root>
<xsl:for-each select="act/acts">
<xsl:variable name="contactid" select="#id"/>
<xsl:for-each select="root/comp/comps">
<xsl:variable name="var" select="boolean(contactid=#id)"/>
</xsl:for-each>
<xsl:choose>
<xsl:when test="$var='true'">
. . . do this . . .
</xsl:when>
<xsl:otherwise>
. . . do that . . .
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
I want to dynamically assign true or false to var and use it inside <xsl:choose> for boolean test. I hope this helps to find a better solution to get rid of for-each also
First thing to note is that variables in XSLT are immutable, and cannot be changed once initialised. The main problem with your XSLT is that you define your variable within an xsl:for-each block and so it only exists within the scope of that block. It is not a global variable. A new variable gets defined each time that can only be used within the xsl:for-each
From looking at your XSLT it looks like you want to iterate over the acts element and perform a certain action depending on whether an comps element exists with the same value. An alternative approach would be to define a key to look up the comps elements, like so
<xsl:key name="comps" match="comps" use="#id" />
Then you can simply check whether a comps element exists like so (assuming you are positioned on an acts element.
<xsl:choose>
<xsl:when test="key('comps', #id)">Yes</xsl:when>
<xsl:otherwise>No</xsl:otherwise>
</xsl:choose>
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="comps" match="comps" use="#id" />
<xsl:template match="/root">
<xsl:apply-templates select="act/acts" />
</xsl:template>
<xsl:template match="acts">
<xsl:choose>
<xsl:when test="key('comps', #id)"><res>Yes</res></xsl:when>
<xsl:otherwise><res>No</res></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to the following (well-formed) XML
<root>
<act>
<acts id="123"/>
</act>
<comp>
<comps id="233"/>
</comp>
</root>
The following is output
No
However, it can often be preferably in XSLT to avoid the use of conditional statements like xsl:choose and xsl:if. Instead, you can structure the XSLT to make use of template matching. Here is the alternate approach
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="comps" match="comps" use="#id" />
<xsl:template match="/root">
<xsl:apply-templates select="act/acts" />
</xsl:template>
<xsl:template match="acts[key('comps', #id)]">
<res>Yes</res>
</xsl:template>
<xsl:template match="acts">
<res>No</res>
</xsl:template>
</xsl:stylesheet>
When applied to the same XML, the same result is output. Do note the more specific template for the acts node will take priority when matching the case where a comps exist.
There are some errors in your xml file, but assuming what you mean is:
<root>
<act><acts id="123"></acts></act>
<comp><comps id="233"></comps></comp>
</root>
Here is a full solution:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<doc>
<xsl:apply-templates select="root/comp/comps"/>
</doc>
</xsl:template>
<xsl:template match="root/comp/comps">
<xsl:variable name="compsid" select="#id"></xsl:variable>
<xsl:choose>
<xsl:when test="count(/root/act/acts[#id=$compsid])>0">Do This</xsl:when>
<xsl:otherwise>Do That</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

Calling the same xsl:template for different node names of the same complex type

I'm trying to keep my xsl DRY and as a result I wanted to call the same template for 2 sections of an XML document which happen to be the same complex type (ContactDetails and AltContactDetails). Given the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<RootNode>
<Name>Bob</Name>
<ContactDetails>
<Address>
<Line1>1 High Street</Line1>
<Town>TownName</Town>
<Postcode>AB1 1CD</Postcode>
</Address>
<Email>test#test.com</Email>
</ContactDetails>
<AltContactDetails>
<Address>
<Line1>3 Market Square</Line1>
<Town>TownName</Town>
<Postcode>EF2 2GH</Postcode>
</Address>
<Email>bob#bob.com</Email>
</AltContactDetails>
</RootNode>
I wrote an XSL Stylesheet as follows:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<PersonsName>
<xsl:value-of select="RootNode/Name"/>
</PersonsName>
<xsl:call-template name="ContactDetails">
<xsl:with-param name="data"><xsl:value-of select="RootNode/ContactDetails"/></xsl:with-param>
<xsl:with-param name="elementName"><xsl:value-of select="'FirstAddress'"/></xsl:with-param>
</xsl:call-template>
<xsl:call-template name="ContactDetails">
<xsl:with-param name="data"><xsl:value-of select="RootNode/AltContactDetails"/></xsl:with-param>
<xsl:with-param name="elementName"><xsl:value-of select="'SecondAddress'"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="ContactDetails">
<xsl:param name="data"></xsl:param>
<xsl:param name="elementName"></xsl:param>
<xsl:element name="{$elementName}">
<FirstLine>
<xsl:value-of select="$data/Address/Line1"/>
</FirstLine>
<Town>
<xsl:value-of select="$data/Address/Town"/>
</Town>
<PostalCode>
<xsl:value-of select="$data/Address/Postcode"/>
</PostalCode>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
When i try to run the style sheet it's complaining to me that I need to:
To use a result tree fragment in a path expression, either use exsl:node-set() or specify version 1.1
I don't want to go to version 1.1.. So does anyone know how to get the exsl:node-set() working for the above example?
Or if someone knows of a better way to apply the same template to 2 different sections then that would also really help me out?
Thanks
Dave
You are rolling this up from the wrong end (the wrong end being nearly always: trying to apply the imperative programming paradigm to XSLT).
This is really easy to do via template matching.
<xsl:template match="RootNode">
<PersonsName>
<xsl:value-of select="Name"/>
</PersonsName>
<xsl:apply-templates select="ContactDetails|AltContactDetails" />
</xsl:template>
<xsl:template match="ContactDetails|AltContactDetails">
<xsl:copy>
<FirstLine>
<xsl:value-of select="Address/Line1"/>
</FirstLine>
<Town>
<xsl:value-of select="Address/Town"/>
</Town>
<PostalCode>
<xsl:value-of select="Address/Postcode"/>
</PostalCode>
</xsl:copy>
</xsl:template>
Let go of the notion that you have to tell the XSLT processor what to do (through making named templates and calling them, "imperative style").
The XSLT processor chooses what templates to call. Starting at the root (/) it recursively checks for matching templates for every node it visits. It traverses your input XML all on its own - your only job is it to supply it with matching templates for those nodes you want to have processed in a special way.
You can drop in a custom template for those nodes that need special treatment and trust your XSLT processor with calling it once they come up. All you need to make sure in your templates is that traversal goes on by declaring an appropriate <xsl:apply-templates />.
Why not make the template
<xsl:template match="ContactDetails|AltContactDetails">
and do the test to determine the output element name inside the template instead?