xsl:result-document instruction throws error when invoking stylesheet with Calabash - xslt

I have an XSL stylesheet that looks like this:
<xsl:template name="xsl:initial-template">
<xsl:for-each-group select="collection($doc-collection)//dr:description" group-by="format-date(dr:date, '[Mn]-[Y]')">
<xsl:result-document href="{current-grouping-key()}.html" method="html" indent="yes">
<xsl:call-template name="page">
<xsl:with-param name="desc" select="current-group()"/>
</xsl:call-template>
</xsl:result-document>
</xsl:for-each-group>
</xsl:template>
(Variables and other templates omitted for brevity.)
When I invoke this using Saxon, everything runs fine and I end up with x documents created through xsl:result-document.
When it is run as part of an XProc pipeline with Calabash as follows:
<p:xslt name="transformation" template-name="xsl:initial-template">
<p:input port="stylesheet">
<p:document href="../xslt/main.xsl"/>
</p:input>
<p:input port="source">
<p:empty/>
</p:input>
<p:with-param name="doc-collection" select="resolve-uri($source)"/>
</p:xslt>
I get a run-time error during the transformation: Cannot execute xsl:result-document while evaluating xsl:variable. I tried stripping down the code to a bare minimum, but I cannot prevent the error from occuring when calling xsl:result-document.
I don't know what variable it is referring to, I can only assume it is created somewhere inside the pipeline?
Working with Saxon EE 9.9.1.5 and Calabash 1.1.30-99 inside Oxygen XML Developer.

Related

XSLT Transformation Issue

I have an issue trying to transform the following. The input has a parent-child relationship which is shown as levels below. The Parent_Identifier tag helps relate the children to the parent. What is the issue with the XSLT transformation? I've used a transformation which was referred to here in this post: Xslt group parent/child, but I can't seem to get it to work.
Input Document
<DBAdapterOutputCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.oracle.com/pcbpel/adapter/db/DBAdapter">
<DBAdapterOutput>
<LEVEL>1</LEVEL>
<IDENTIFIER>9536162</IDENTIFIER>
<PARENT_IDENTIFIER xsi:nil="true"/>
<LINE_NUMBER>1.1.0</LINE_NUMBER>
</DBAdapterOutput>
<DBAdapterOutput>
<LEVEL>2</LEVEL>
<IDENTIFIER>9536165</IDENTIFIER>
<PARENT_IDENTIFIER>9536162</PARENT_IDENTIFIER>
<LINE_NUMBER>1.1.1</LINE_NUMBER>
<ORDER_NUMBER>1554828</ORDER_NUMBER>
</DBAdapterOutput>
<DBAdapterOutput>
<LEVEL>2</LEVEL>
<IDENTIFIER>9536173</IDENTIFIER>
<PARENT_IDENTIFIER>9536162</PARENT_IDENTIFIER>
<LINE_NUMBER>1.1.7</LINE_NUMBER>
<ORDER_NUMBER>1554828</ORDER_NUMBER>
</DBAdapterOutput>
<DBAdapterOutput>
<LEVEL>3</LEVEL>
<IDENTIFIER>1227973</IDENTIFIER>
<PARENT_IDENTIFIER>9536165</PARENT_IDENTIFIER>
<LINE_NUMBER>1.1.4</LINE_NUMBER>
<ORDER_NUMBER>1554828</ORDER_NUMBER>
</DBAdapterOutput>
<DBAdapterOutput>
<LEVEL>3</LEVEL>
<IDENTIFIER>1275015</IDENTIFIER>
<PARENT_IDENTIFIER>9536165</PARENT_IDENTIFIER>
<LINE_NUMBER>1.1.4</LINE_NUMBER>
<ORDER_NUMBER>1554828</ORDER_NUMBER>
</DBAdapterOutput>
</DBAdapterOutputCollection>
Expected Output Document
<WMSAssetInterface_Input xmlns="http://siebel.com/CustomUI">
<ListOfAsset xmlns="http://www.siebel.com/xml/ThinComergentAsset">
<ListOfAssetHeader>
<AssetHeader>
<IntegrationId>9536162 1.1.0</IntegrationId>
<ProductName>1.1.0</ProductName>
<ListOfAssetItem>
<AssetItem>
<IntegrationId>9536162 1.1.0 Level=1</IntegrationId>
<ProductName>1.1.0</ProductName>
<ListOfAssetItem>
<AssetItem>
<IntegrationId>9536165 1.1.1 Level=2</IntegrationId>
<ProductName>1.1.1</ProductName>
<ListOfAssetItem>
<AssetItem>
<IntegrationId>1227973 1.1.4 Level=3</IntegrationId>
<ProductName>1.1.4</ProductName>
</AssetItem>
<AssetItem>
<IntegrationId>1275015 1.1.4 Level=3</IntegrationId>
<ProductName>1.1.4</ProductName>
</AssetItem>
</ListOfAssetItem>
</AssetItem>
<AssetItem>
<IntegrationId>9536173 1.1.7 Level=2</IntegrationId>
<ProductName>1.1.7</ProductName>
</AssetItem>
</ListOfAssetItem>
</AssetItem>
</ListOfAssetItem>
</AssetHeader>
<ListOfAssetHeader>
</ListOfAsset>
</WMSAssetInterface_Input>
Here's the XSLT I'm trying to use; why isn't this working?
<xsl:stylesheet version="1.0" xmlns:ns0="http://xmlns.oracle.com/pcbpel/adapter/db/DBAdapter" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xsdLocal1="http://www.siebel.com/xml/ThinComergentAsset" xmlns:tns="http://siebel.com/CustomUI" exclude-result-prefixes="tns" >
<xsl:template match="/ns0:DBAdapterOutputCollection">
<tns:WMSAssetInterface_Input>
<xsdLocal1:ListOfAsset>
<xsdLocal1:ListOfAssetHeader>
<xsdLocal1:AssetHeader>
<xsdLocal1:IntegrationId>
<xsl:value-of select='concat(ns0:DBAdapterOutput/ns0:IDENTIFIER," ",ns0:DBAdapterOutput/ns0:LINE_NUMBER," Level=",ns0:DBAdapterOutput/ns0:LEVEL)'/>
</xsdLocal1:IntegrationId>
<xsdLocal1:ProductName>
<xsl:value-of select="ns0:DBAdapterOutput/ns0:LINE_NUMBER"/>
</xsdLocal1:ProductName>
<xsl:apply-templates select="ns0:DBAdapterOutputCollection/ns0:DBAdapterOutput[string-length(ns0:PARENT_IDENTIFIER)=0]" />
</xsdLocal1:AssetHeader>
</xsdLocal1:ListOfAssetHeader>
</xsdLocal1:ListOfAsset>
</tns:WMSAssetInterface_Input>
</xsl:template>
<xsl:template match="ns0:DBAdapterOutput">
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:IntegrationId>
<xsl:value-of select='concat(ns0:IDENTIFIER," ",ns0:LINE_NUMBER," Level=",ns0:LEVEL)'/>
</xsdLocal1:IntegrationId>
<xsdLocal1:PartName>
<xsl:value-of select="ns0:LINE_NUMBER"/>
</xsdLocal1:PartName>
<xsl:variable name="children" select="parent::*/ns0:DBAdapterOutput[ns0:PARENT_IDENTIFIER=current()/ns0:IDENTIFIER]" />
<xsl:if test="$children">
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsl:apply-templates select="$children" />
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
</xsl:if>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
</xsl:template>
</xsl:stylesheet>
Two compile time errors:
The tns: prefix is not bound to any namespace.
This line from your style-sheet is nonsense.
<xsl:value-of select='concat(/ns0:IDENTIFIER," ",/ns0:LINE_NUMBER," Level=",/ns0:DBAdapterOutput/ns0:LEVEL')'/>
The close tag for your style-sheet is missing.
Update
I fixed a couple of errors in the style-sheet:
<xsl:template match="ns0:DBAdapterOutput/" /> is not valid. Remove the final / .
Similarly with <xsl:template match="/ns0:DBAdapterOutputCollection">
Update
The technique here is to use xsl:apply-templates recursively to drill down through the levels. Records are matched up with their parents just by comparing the two link fields, but an alternative method would be to use keys.
This XSLT 1.0 style-sheet...
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns0="http://xmlns.oracle.com/pcbpel/adapter/db/DBAdapter"
xmlns:xsdLocal1="http://www.siebel.com/xml/ThinComergentAsset"
exclude-result-prefixes="xsl xsdLocal1 ns0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<WMSAssetInterface_Input xmlns="http://siebel.com/CustomUI">
<ListOfAsset xmlns="http://www.siebel.com/xml/ThinComergentAsset">
<ListOfAssetHeader>
<xsl:apply-templates select="*/ns0:DBAdapterOutput[ns0:LEVEL=1]" />
</ListOfAssetHeader>
</ListOfAsset>
</WMSAssetInterface_Input>
</xsl:template>
<xsl:template match="ns0:DBAdapterOutput[ns0:LEVEL=1]"
xmlns="http://www.siebel.com/xml/ThinComergentAsset">
<AssetHeader>
<IntegrationId>
<xsl:value-of select="concat(ns0:IDENTIFIER,' ',ns0:LINE_NUMBER)" />
</IntegrationId>
<ProductName>
<xsl:value-of select="ns0:LINE_NUMBER" />
</ProductName>
<ListOfAssetItem>
<xsl:apply-templates select="../ns0:DBAdapterOutput
[ ns0:LEVEL=
(current()/ns0:LEVEL+1)]" />
</ListOfAssetItem>
</AssetHeader>
</xsl:template>
<xsl:template match="ns0:DBAdapterOutput[ns0:LEVEL > 1]"
xmlns="http://www.siebel.com/xml/ThinComergentAsset">
<AssetItem>
<IntegrationId>
<xsl:value-of select="concat(ns0:IDENTIFIER,' ',ns0:LINE_NUMBER,
' Level=', ns0:LEVEL/text()-1)" />
</IntegrationId>
<ProductName>
<xsl:value-of select="ns0:LINE_NUMBER" />
</ProductName>
<xsl:if test="../ns0:DBAdapterOutput
[ns0:PARENT_IDENTIFIER = current()/ns0:IDENTIFIER]">
<ListOfAssetItem>
<xsl:apply-templates select="../ns0:DBAdapterOutput
[ns0:PARENT_IDENTIFIER = current()/ns0:IDENTIFIER]" />
</ListOfAssetItem>
</xsl:if>
</AssetItem>
</xsl:template>
</xsl:stylesheet>
...when applied to the sample input, will yield...
<WMSAssetInterface_Input xmlns="http://siebel.com/CustomUI">
<ListOfAsset xmlns="http://www.siebel.com/xml/ThinComergentAsset">
<ListOfAssetHeader>
<AssetHeader>
<IntegrationId>9536162 1.1.0</IntegrationId>
<ProductName>1.1.0</ProductName>
<ListOfAssetItem>
<AssetItem>
<IntegrationId>9536165 1.1.1 Level=1</IntegrationId>
<ProductName>1.1.1</ProductName>
<ListOfAssetItem>
<AssetItem>
<IntegrationId>1227973 1.1.4 Level=2</IntegrationId>
<ProductName>1.1.4</ProductName>
</AssetItem>
<AssetItem>
<IntegrationId>1275015 1.1.4 Level=2</IntegrationId>
<ProductName>1.1.4</ProductName>
</AssetItem>
</ListOfAssetItem>
</AssetItem>
<AssetItem>
<IntegrationId>9536173 1.1.7 Level=1</IntegrationId>
<ProductName>1.1.7</ProductName>
</AssetItem>
</ListOfAssetItem>
</AssetHeader>
</ListOfAssetHeader>
</ListOfAsset>
</WMSAssetInterface_Input>
This is slightly different to your stated expected output, but only because, IMHO, of mistakes in the listed expected output. I believe that this delivers what you want.
First, an observation about the unreadability of the code -- please in future questions provide well indented code with short lines!
There is one major issue that can be easily corrected.
Change this:
<xsl:apply-templates select=
"ns0:DBAdapterOutputCollection/ns0:DBAdapterOutput
[string-length(ns0:PARENT_IDENTIFIER)=0]" />
to this:
<xsl:apply-templates select=
"ns0:DBAdapterOutput
[string-length(ns0:PARENT_IDENTIFIER)=0]" />
The XSLT instruction that has to be modified is in a template with current node the top
ns0:DBAdapterOutputCollection element. It specifies that templates should be applied to any of its grand-children ns0:DBAdapterOutput element that satisfies the condition in the predicate and that is a child of any ns0:DBAdapterOutputCollection child of the current node.
However, the current node (the top ns0:DBAdapterOutputCollection element) has no children ns0:DBAdapterOutputCollection children and the no nodes are selected for execution by this xsl:apply-templates instruction.
After the above simple code-change is made, the result from the transformation seems like what probably is wanted:
<?xml version="1.0" encoding="utf-8"?>
<tns:WMSAssetInterface_Input xmlns:tns="http://siebel.com/CustomUI" xmlns:ns0="http://xmlns.oracle.com/pcbpel/adapter/db/DBAdapter" xmlns:xsdLocal1="http://www.siebel.com/xml/ThinComergentAsset">
<xsdLocal1:ListOfAsset>
<xsdLocal1:ListOfAssetHeader>
<xsdLocal1:AssetHeader>
<xsdLocal1:IntegrationId>9536162 1.1.0 Level=1</xsdLocal1:IntegrationId>
<xsdLocal1:ProductName>1.1.0</xsdLocal1:ProductName>
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:IntegrationId>9536162 1.1.0 Level=1</xsdLocal1:IntegrationId>
<xsdLocal1:PartName>1.1.0</xsdLocal1:PartName>
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:IntegrationId>9536165 1.1.1 Level=2</xsdLocal1:IntegrationId>
<xsdLocal1:PartName>1.1.1</xsdLocal1:PartName>
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:IntegrationId>1227973 1.1.4 Level=3</xsdLocal1:IntegrationId>
<xsdLocal1:PartName>1.1.4</xsdLocal1:PartName>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:IntegrationId>1275015 1.1.4 Level=3</xsdLocal1:IntegrationId>
<xsdLocal1:PartName>1.1.4</xsdLocal1:PartName>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
<xsdLocal1:ListOfAssetItem>
<xsdLocal1:AssetItem>
<xsdLocal1:IntegrationId>9536173 1.1.7 Level=2</xsdLocal1:IntegrationId>
<xsdLocal1:PartName>1.1.7</xsdLocal1:PartName>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
</xsdLocal1:AssetItem>
</xsdLocal1:ListOfAssetItem>
</xsdLocal1:AssetHeader>
</xsdLocal1:ListOfAssetHeader>
</xsdLocal1:ListOfAsset>
</tns:WMSAssetInterface_Input>

XPath/XSLT nested predicates: how to get the context of outer predicate?

It seems that this question was not discussed on stackoverflow before, save for Working With Nested XPath Predicates ... Refined where the solution not involving nested predicates was offered.
So I tried to write the oversimplified sample of what I'd like to get:
Input:
<root>
<shortOfSupply>
<food animal="doggie"/>
<food animal="horse"/>
</shortOfSupply>
<animalsDictionary>
<cage name="A" animal="kittie"/>
<cage name="B" animal="dog"/>
<cage name="C" animal="cow"/>
<cage name="D" animal="zebra"/>
</animals>
</root>
Output:
<root>
<hungryAnimals>
<cage name="B"/>
<cage name="D"/>
</hungryAnimals>
</root>
or, alternatively, if there is no intersections,
<root>
<everythingIsFine/>
</root>
And i want to get it using a nested predicates:
<xsl:template match="cage">
<cage>
<xsl:attribute name="name">
<xsl:value-of select="#name"/>
</xsl:attribute>
</cage>
</xsl:template>
<xsl:template match="/root/animalsDictionary">
<xsl:choose>
<!-- in <food> in <cage> -->
<xsl:when test="cage[/root/shortOfSupply/food[ext:isEqualAnimals(./#animal, ?????/#animal)]]">
<hungryAnimals>
<xsl:apply-templates select="cage[/root/shortOfSupply/food[ext:isEqualAnimals(#animal, ?????/#animal)]]"/>
</hungryAnimals>
</xsl:when>
<xsl:otherwise>
<everythingIsFine/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
So what should i write in place of that ??????
I know i could rewrite the entire stylesheet using one more template and extensive usage of variables/params, but it makes even this stylesheet significantly more complex, let alone the real stylesheet i have for real problem.
It is written in XPath reference that the dot . sign means the current context node, but it doesn't tell whether there is any possibility to get the node of context before that; and i just can't believe XPath is missing this obvious feature.
XPath 2.0 one-liner:
for $a in /*/animalsDictionary/cage
return
if(/*/shortOfSupply/*[my:isA($a/#animal, #animal)])
then $a
else ()
When applied on the provided XML document selects:
<cage name="B"/>
<cage name="D"/>
One cannot use a single XPath 1.0 expression to find that a given cage contains a hungry animal.
Here is an XSLT solution (XSLT 2.0 is used only to avoid using an extension function for the comparison -- in an XSLT 1.0 solution one will use an extension function for the comparison and the xxx:node-set() extension to test if the RTF produced by applying templates in the body of the variable contains any child element):
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:my="my:my" exclude-result-prefixes="xs my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<my:Dict>
<a genName="doggie">
<name>dog</name>
<name>bulldog</name>
<name>puppy</name>
</a>
<a genName="horse">
<name>horse</name>
<name>zebra</name>
<name>pony</name>
</a>
<a genName="cat">
<name>kittie</name>
<name>kitten</name>
</a>
</my:Dict>
<xsl:variable name="vDict" select=
"document('')/*/my:Dict/a"/>
<xsl:template match="/">
<root>
<xsl:variable name="vhungryCages">
<xsl:apply-templates select=
"/*/animalsDictionary/cage"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$vhungryCages/*">
<hungryAnimals>
<xsl:copy-of select="$vhungryCages"/>
</hungryAnimals>
</xsl:when>
<xsl:otherwise>
<everythingIsFine/>
</xsl:otherwise>
</xsl:choose>
</root>
</xsl:template>
<xsl:template match="cage">
<xsl:if test="
/*/shortOfSupply/*[my:isA(current()/#animal,#animal)]">
<cage name="{#name}"/>
</xsl:if>
</xsl:template>
<xsl:function name="my:isA" as="xs:boolean">
<xsl:param name="pSpecName" as="xs:string"/>
<xsl:param name="pGenName" as="xs:string"/>
<xsl:sequence select=
"$pSpecName = $vDict[#genName = $pGenName]/name"/>
</xsl:function>
</xsl:stylesheet>
When this transformation is applied on the provided XML document (corrected to be well-formed):
<root>
<shortOfSupply>
<food animal="doggie"/>
<food animal="horse"/>
</shortOfSupply>
<animalsDictionary>
<cage name="A" animal="kittie"/>
<cage name="B" animal="dogs"/>
<cage name="C" animal="cow"/>
<cage name="D" animal="zebras"/>
</animalsDictionary>
</root>
the wanted, correct result is produced:
<root>
<hungryAnimals>
<cage name="B"/>
<cage name="D"/>
</hungryAnimals>
</root>
Explanation: Do note the use of the XSLT current() function.
XPath 1.0 is not "relationally complete" - it can't do arbitrary joins. If you're in XSLT, you can always get round the limitations by binding variables to intermediate nodesets, or (sometimes) by using the current() function.
XPath 2.0 introduces range variables, which makes it relationally complete, so this limitation has gone.
Doesn't <xsl:when test="cage[#animal = /root/shortOfSupply/food/#animal]"> suffice to express your test condition?
Notice The dot operator in XPath is related to the current context. In XSLT the current template context_ is given by the function current(), which most of the time (not always) coincides with the ..
You can perform the test (and the apply templates as well), using the parent axis abbreviation (../):
cage[#animal=../../shortOfSupply/food/#animal]
Moreover the match pattern in the the first template is wrong, it should be relative to the root:
/root/animalsDictionary
#Martin suggestion is also obviously correct.
Your final template slightly modified:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="root/animalsDictionary">
<xsl:choose>
<xsl:when test="cage[#animal=../../shortOfSupply/food/#animal]">
<hungryAnimals>
<xsl:apply-templates select="cage[#animal
=../../shortOfSupply/food/#animal]"/>
</hungryAnimals>
</xsl:when>
<xsl:otherwise>
<everythingIsFine/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="cage">
<cage name="{#name}"/>
</xsl:template>
</xsl:stylesheet>

keeping characters un-escaped in attributes when processing with XSLT

I'm transforming an ivy.xml file with XSLT, I basically want to update the rev attribute of a specific dependency tag. My problem is with the conf attribute, I want it to stay exactly the same, unfortunately > gets converted to >. My ivy file looks like this:
<ivy-module version="1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
<info organisation="foo" module="libfoo" revision="1.0.1"/>
<configurations>
</configurations>
<publications>
<artifact name="libfoo" type="jar" conf="default" />
</publications>
<dependencies>
<dependency org="easymock" name="easymock" rev="2.5.2" conf="test->default,class-extension"/>
</dependencies>
</ivy-module>
my XSL looks like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output />
<xsl:param name="dependency.rev" />
<xsl:param name="dependency.org" />
<xsl:param name="dependency.name" />
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="dependency">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
<xsl:choose>
<xsl:when test="#name=$dependency.name">
<xsl:attribute name="rev">
<xsl:value-of select="$dependency.rev" />
</xsl:attribute>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
and my output looks like this:
<dependency org="easymock" name="easymock" rev="2.5.2" conf="test->default,class-extension"/>
I've tried setting disable-output-escaping="no" on an explicit set of the conf attribute, but that didn't work
<xsl:attribute name="conf">
<xsl:value-of select="#conf" disable-output-escaping="yes" />
</xsl:attribute>
My problem is with the conf attribute,
I want it to stay exactly the same,
unfortunately > gets converted to
>.
You can't. As per the W3 XSLT Spec:
"It is an error for output escaping to be disabled for a text node that is used for something other than a text node in the result tree. Thus, it is an error to disable output escaping for an xsl:value-of or xsl:text element that is used to generate the string-value of a comment, processing instruction or attribute node"
There is nothing problematic in the conf attribute containing test->.
In fact, both
conf="test->default,class-extension"
and
conf="test->default,class-extension"
have exactly the same string value.
To verify this, just apply the following simple transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/*">
"<xsl:value-of select="#conf"/>"
</xsl:template>
</xsl:stylesheet>
to the "bad" result:
<dependency org="easymock"
name="easymock"
rev="2.5.2"
conf="test->default,class-extension"
/>
and what you get is:
"test->default,class-extension"
This is exactly the same as coding   instead of -- these are exactly the same characters, but specified in different ways.

Where do I put an XSL function in an XSL document?

I have an XSL style sheet for which I need to add some custom string manipulation using an xsl:function. But I am having trouble trying to work out where to put the function in my document.
My XSL simplified looks like this,
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:my="myFunctions" xmlns:d7p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:import href="Master.xslt"/>
<xsl:template match="/">
<fo:root xmlns:fo="http://www.w3.org/1999/XSL/Format">
<!-- starts actual layout -->
<fo:page-sequence master-reference="first">
<fo:flow flow-name="xsl-region-body">
<!-- this defines a title level 1-->
<fo:block xsl:use-attribute-sets="heading">
HelloWorld
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
</xsl:template>
</xsl:stylesheet>
And I want to put in a simple function, say,
<xsl:function name="my:helloWorld">
<xsl:text>Hello World!</xsl:text>
</xsl:function>
But I cannot work out where to put the function, when I put it under the node I get an error saying 'xsl:function' cannot be a child of the 'xsl:stylesheet' element., and if I put it under the node I get a similar error.
Where should I put the function? Idealy I would like to put my functions in an external file and import them into my xsl files.
There is no xsl:function in XSL version 1.0. You have to create a named template
<xsl:template name="helloWorld">
<xsl:text>Hello World!</xsl:text>
</xsl:template>
(...)
<xsl:template match="something">
<xsl:call-template name="helloWorld"/>
</xsl:template>
You can upgrade the stylesheet version to 2.0
Then in stylesheet declaration specify as
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:func="http://www.**.com">
** Your choice you can specify anything as your wish
then below this specify your function
<xsl:function name="func:helloWorld">
<xsl:text>Hello World!</xsl:text>
</xsl:function>
Then in template you can use it as
<xsl:template match="/">
<xsl:value-of select="func:helloWorld"/>
</xsl:template>

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

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.