XSLT Node availability check - xslt

I want to check a variable have any node or any attribute.
XSL:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:variable name="testvar">
<test><name first="Isaac" last="Sivakumar" middle="G"></name></test>
</xsl:variable>
<xsl:choose>
<xsl:when test="normalize-space($testvar)">
<xsl:value-of select="$testvar"></xsl:value-of>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'NO XML DATA AVAILABLE'"></xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When I try to run the above code I am getting "NO XML DATA AVIALABLE." I need to check weather a variable has any node / any attributes irrespective of it has data or not.
Can you please help me to fix this.

With XSLT 1.0 your variable has a value of type "result tree fragment", you need to use an extension function to convert it to a node set first to be able to address nodes e.g.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common" version="1.0">
<xsl:template match="/">
<xsl:variable name="testvar">
<test><name first="Isaac" last="Sivakumar" middle="G"></name></test>
</xsl:variable>
<xsl:choose>
<xsl:when test="exsl:node-set($testvar)/node()">
<xsl:copy-of select="$testvar"></xsl:value-of>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'NO XML DATA AVAILABLE'"></xsl:value-of>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Using normalize-space or value-of does not make much sense given that your XML in the result tree fragment has all data in attributes and no text nodes with data.
And the test test="exsl:node-set($testvar)/node()" is just an example, it could of course use e.g. test="exsl:node-set($testvar)//name" to test for a specific element like a name element.
Given XSLT 1.0 but EXSLT common support it might be better to check with http://www.exslt.org/exsl/functions/object-type/index.html e.g.
<xsl:choose>
<xsl:when test="exsl:object-type($testvar) = 'string' and $testvar = ''">
<xsl:value-of select="'NO XML DATA AVAILABLE'"/>
</xsl:when>
<xsl:when test="exsl:object-type($testvar) = 'node-set'">
<xsl:copy-of select="$testvar"/>
</xsl:when>
</xsl:choose>
Given XSLT 2.0 I would simply check e.g if ($testvar instance of xs:string and $testvar = '') then 'NO XML DATA AVAILABLE' else $testvar.

Related

choosing specific column value from the input xml

i have the below xml as input for which i have to do the xsl transformation
<emml>
<tradeEventHeader>
<tradeIdentifier>
<tradeId>104823343913</tradeId>
<systemReference>RDS</systemReference>
<systemDomainName>Internal</systemDomainName>
</tradeIdentifier>
<tradeStateIdentifier>
<tradeStateId>Validated</tradeStateId>
<systemReference>RDS</systemReference>
<tradeStateIdClassificationScheme>Vn State</tradeStateIdClassificationScheme>
</tradeStateIdentifier>
<tradeStateIdentifier>
<tradeStateId>Pending</tradeStateId>
<systemReference>Swapswire</systemReference>
<tradeStateIdClassificationScheme>Mang State</tradeStateIdClassificationScheme>
</tradeStateIdentifier>
<tradeStateIdentifier>
<tradeStateId>accpt_novated_sw</tradeStateId>
<systemReference>RDS</systemReference>
<tradeStateIdClassificationScheme>Clearing State</tradeStateIdClassificationScheme>
</tradeStateIdentifier>
</tradeEventHeader>
<emmlExtension systemId="RDS YTO">
<emmlMediumString idref="legId1" name="Roll Date Option">Short Initial</emmlMediumString>
</emmlExtension>
</emml>
as shown above in the input xml basically my objective is to identify the value of tradeStateIdClassificationScheme parameter and if the value of this parameter is equal to 'Clearing state' then with correspond to that i have to check the value of another column tradeStateId and if the value of the column tradeStateId starts with accpt_novated_sw then in that case we need to return true string and for rest other i need to return false string ..
i have come up with the below template in xslt 1.0 , please advise is it correct approach..
calling template :-
<isClearedNovated>
<xsl:call-template name="cleared_novated">
<xsl:with-param name="tradeStateId" select="emml/*/*/tradeStateIdentifier" />
</xsl:call-template>
</isClearedNovated>
called template :-
<xsl:template name="cleared_novated">
<xsl:param name="tradeStateId" />
<xsl:for-each select="$tradeStateId/tradeStateIdClassificationScheme">
<xsl:choose>
<xsl:when test="$tradeStateId[starts-with(tradeStateIdClassificationScheme,'accpt')] and systemReference='RDS'">
<xsl:value-of select="'true'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'false'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
I don't really understand, what exactly your needs are, but your XSLT probably does not what you want - I suspect it does nothing...
So maybe we can start with the suggestion below and you can tell, what has to be refined:
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="//tradeStateIdClassificationScheme"/>
</xsl:template>
<xsl:template match="tradeStateIdClassificationScheme[
. = 'Clearing State' and
../tradeStateId = 'accpt_novated_sw' and
../systemReference = 'RDS'
]">
<xsl:value-of select="concat(.,': true
')"/>
</xsl:template>
<xsl:template match="tradeStateIdClassificationScheme">
<xsl:value-of select="concat(.,': false
')"/>
</xsl:template>
<xsl:template match="#*|*"/>
</xsl:transform>
You find two templates dealing with tradeStateIdClassificationScheme, one matches your conditions, and one for all others.
Note that you didn't write about the contents of systemReference, while your trial template addresses this element. Therefore, I added this condition as well.
The output in this version is:
Vn State: false
Mang State: false
Clearing State: true

Structural requirements when using "except" in XPATH/XSL

I am having trouble when using "except" in xpath. Here is the chunk of problem code. (I tried to simplify as much as possible without obscuring the whole problem).:
<!--First, create a variable containing some nodes that we want to filter out.
(I'm gathering elements that are missing child VALUE elements
and whose child DOMAIN and VARIABLE elements only occur once
in the parent list of elements.)
I've confirmed that this part does generate the nodes I want,
but maybe this is the incorrect result structure?-->
<xsl:variable name="badValues">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN and VARIABLE=current()/VARIABLE])=1">
<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<!--Next Loop over the original nodes, minus those bad nodes.
For some reason, this loops over all nodes and does not filter out the bad nodes.-->
<xsl:for-each select="$root/A except $badValues/A"> ...
When you create an xsl:variable without using #select and do not specify the type with the #as, it will create the variable as a temporary tree.
You want to create a sequence of nodes, so that when they are compared in the except operator, they are "seen" as the same nodes. You can do this by specifying as="node()*" for the xsl:variable and by using xsl:sequence instead of xsl:copy-of:
<xsl:variable name="badValues" as="node()*">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:sequence select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
Alternatively, if you were to use a #select and eliminate the xsl:for-each it would also work. As Martin Honnen suggested, you could use an xsl:key and select the values like this:
<xsl:key name="by-dom-and-var" match="A" use="concat(DOMAIN, '|', VARIABLE)"/>
Then change your badValues to this:
<xsl:variable name="badValues"
select="$root/A[not(VALUE)]
[count(key('by-dom-and-var',
concat(DOMAIN, '|', VARIABLE))/VARIABLE) = 1]"/>>
You can see the difference in the identity of the nodes by using the generate-id() function as you iterate over the items by executing this stylesheet:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:variable name="root" select="*" as="item()*"/>
<xsl:variable name="originalBadValues">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:copy-of select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="badValues" as="node()*">
<xsl:for-each select="$root/A[not(VALUE)]">
<xsl:choose>
<xsl:when test="count($root/A[DOMAIN=current()/DOMAIN
and VARIABLE=current()/VARIABLE])=1">
<xsl:sequence select="."/>
</xsl:when>
</xsl:choose>
</xsl:for-each>
</xsl:variable>
<!--These are the generated ID values of all the A elements-->
<rootA>
<xsl:value-of select="$root/A/generate-id()"
separator=", "/>
</rootA>
<!--These are the generated ID values for
the original $badValues/A -->
<originalBadValues>
<xsl:value-of select="$originalBadValues/A/generate-id()"
separator=", " />
</originalBadValues>
<!--These are the generated ID values for
the correct selection of $badValues-->
<badValues>
<xsl:value-of select="$badValues/generate-id()"
separator=", " />
</badValues>
<!--The generated ID values for the result of
the except operator filter-->
<final>
<xsl:value-of select="($root/A except $badValues)/generate-id()"
separator=", "/>
</final>
</xsl:template>
</xsl:stylesheet>
Executed against this XML file:
<doc>
<A>
<VALUE>skip me</VALUE>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
<A>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
<A>
<DOMAIN>b</DOMAIN>
<VARIABLE>b</VARIABLE>
</A>
<A>
<DOMAIN>c</DOMAIN>
<VARIABLE>c</VARIABLE>
</A>
<A>
<DOMAIN>a</DOMAIN>
<VARIABLE>a</VARIABLE>
</A>
</doc>
It produces the following output:
<rootA>d1e3, d1e15, d1e24, d1e33, d1e42</rootA>
<originalBadValues>d2e1, d2e9</originalBadValues>
<badValues>d1e24, d1e33</badValues>
<final>d1e3, d1e15, d1e42</final>

xslt 1.0 rewrite values of a node according to a map

While transforming a document, I need to 'look up' certain node contents in a 'map', and write those values.
I inlined my 'map' in the transformation.
<xsl:variable name="inlinedmap">
<kat id="stuff">value</kat>
<!-- ... -->
</xsl:variable>
<xsl:variable name="map" select="document('')/xsl:stylesheet/xsl:variable[#name='inlinedmap']" />
<xsl:template match="/">
<xsl:for-each select="/*/foo">
<!-- 'bar' contents should equal to contents of 'kat' -->
<xsl:variable name="g" select="$map/key[.=bar]"/>
<xsl:choose>
<xsl:when test="$g != ''">
<xsl:value-of select="$g/#id"/>
</xsl:when>
<xsl:otherwise>
ERROR
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
I'm always getting ERROR value.
I can't put map value's into attributes, because they contain letters that get escaped.
How can I make it work?
I think there are a few problems here:
You seem to be looking for key elements in your variable, but they're called kat there (typo?)
You seem to be trying to reference the bar child of the context node inside the loop, but you need to use current() to do that
You should create this map as elements in your own namespace instead of an xsl:variable
Here's a complete example. This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my">
<my:vars>
<kat id="stuff">value</kat>
<!-- ... -->
</my:vars>
<xsl:variable name="map" select="document('')/*/my:vars/*"/>
<xsl:template match="/">
<xsl:for-each select="/*/foo">
<!-- 'bar' contents should equal to contents of 'kat' -->
<xsl:variable name="g" select="$map[.=current()/bar]"/>
<xsl:choose>
<xsl:when test="$g != ''">
<xsl:value-of select="$g/#id"/>
</xsl:when>
<xsl:otherwise>
ERROR
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Applied to this input:
<root>
<foo><bar>value</bar></foo>
<foo><bar>value1</bar></foo>
<foo><bar>value2</bar></foo>
<foo><bar>value3</bar></foo>
</root>
Produces this output (one match):
stuff
ERROR
ERROR
ERROR

How to store the current path in xsl?

I would like to store the path of the current node so I can reused it in an expression in XSLT. Is it possible?
<!-- . into $path? -->
<xsl:value-of select="$path" />
Hi, I would like to store the path of
the current node so I can reused it in
an expression in XSLT. Is it possible?
It is possible for any given node to construct an XPath expression that, when evaluated, selects exactly this node. In fact more than one XPath expression exists that selects the same node.
See this answer for the exact XSLT code that constructs such an XPath expression.
The problem is that this XPath expression cannot be evaluated during the same transformation in XSLT 1.0 or XSLT 2.0, unless the EXSLT extension function dyn:evaluate is used (and very few XSLT 1.0 processors implement dyn:evaluate() ).
What you want can be achieved in an easier way in XSLT using the <xsl:variable> instruction:
<xsl:variable name="theNode" select="."/>
This variable can be referenced anywhere in its scope as $theNode, and can be passed as parameter when applying or calling templates.
No, this is not possible with vanilla XSLT 1.0. There is no easy way to retrieve an XPath expression string for a given node, and there is definitely no way to evaluate a string that looks like XPath as if it was XPath.
There are extensions that support dynamic evaluation of XPath expressions, but these are not compatible with every XSLT processor.
In any case, if you provide more detail around what you are actually trying to do, there might be another way to do it.
As #Dimitre and #Tomalak have point out, I don't think it has some value in the same transformation to obtain a string representing an XPath expression for a given node, and then select the node "parsing" such string. I could see some value in performing those operations in different transformations.
Besides that, this stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="/">
<xsl:for-each select=".|//node()|//#*">
<xsl:variable name="vPath">
<xsl:apply-templates select="." mode="getPath"/>
</xsl:variable>
<xsl:value-of select="concat($vPath,'
')"/>
<xsl:call-template name="select">
<xsl:with-param name="pPath" select="$vPath"/>
</xsl:call-template>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:template>
<xsl:template match="/|node()|#*" mode="getPath" name="getPath">
<xsl:apply-templates select="parent::*" mode="getPath"/>
<xsl:text>/</xsl:text>
<xsl:choose>
<xsl:when test="self::*">
<xsl:value-of select="concat(name(),'[',
count(preceding-sibling::*
[name() =
name(current())]) + 1,
']')"/>
</xsl:when>
<xsl:when test="count(.|../#*)=count(../#*)">
<xsl:value-of select="concat('#',name())"/>
</xsl:when>
<xsl:when test="self::text()">
<xsl:value-of
select="concat('text()[',
count(preceding-sibling::text()) + 1,
']')"/>
</xsl:when>
<xsl:when test="self::comment()">
<xsl:value-of
select="concat('comment()[',
count(preceding-sibling::comment()) + 1,
']')"/>
</xsl:when>
<xsl:when test="self::processing-instruction()">
<xsl:value-of
select="concat('processing-instruction()[',
count(preceding-sibling::
processing-instruction()) + 1,
']')"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="select">
<xsl:param name="pPath"/>
<xsl:param name="pContext" select="/"/>
<xsl:param name="pInstruction" select="'value-of'"/>
<xsl:variable name="vPosition"
select="number(
substring-before(
substring-after($pPath,
'['),
']'))"/>
<xsl:variable name="vTest"
select="substring-before(
substring-after($pPath,
'/'),
'[')"/>
<xsl:variable name="vPath" select="substring-after($pPath,']')"/>
<xsl:choose>
<xsl:when test="$vPath">
<xsl:call-template name="select">
<xsl:with-param name="pPath" select="$vPath"/>
<xsl:with-param name="pContext"
select="$pContext/*[name()=$vTest]
[$vPosition]"/>
<xsl:with-param name="pInstruction"
select="$pInstruction"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vContext"
select="$pContext/node()
[self::*[name()=$vTest]|
self::comment()[$vTest='comment()']|
self::text()[$vTest='text()']|
self::processing-instruction()
[$vTest =
'processing-instruction()']]
[$vPosition]|
$pContext[$pPath='/']|
$pContext/#*[name() =
substring($pPath,3)]
[not($vTest)]"/>
<xsl:choose>
<xsl:when test="$pInstruction='value-of'">
<xsl:value-of select="$vContext"/>
</xsl:when>
<xsl:when test="$pInstruction='copy-of'">
<xsl:copy-of select="$vContext"/>
</xsl:when>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
With this input:
<?somePI pseudoAttributes?>
<root>
<!-- This is a comment -->
<node attribute="Value">text</node>
</root>
Output:
/
text
/processing-instruction()[1]
pseudoAttributes
/root[1]
text
/root[1]/comment()[1]
This is a comment
/root[1]/node[1]
text
/root[1]/node[1]/#attribute
Value
/root[1]/node[1]/text()[1]
text

XSLT Assignment of element value to variable: Works with Altova XML Spy, but fails in .NET app

I want to tightly grasp the hair on the back of a Microsoft employee's head, using it as leverage to pound his head forcefully and repeatedly against a hard surface! That would make me feel nearly as good as solving this problem right now.
I've got a simple XML message that looks like this:
<?xml version="1.0" encoding="utf-8"?>
<message>
<cmd id="instrument_status">
<status_id>1</status_id>
</cmd>
</message>
A web service on the device I'm working with returns several such messages and I'm converting them to a different format. For the above message the new format would look like the following:
<?xml version="1.0" encoding="UTF-8"?>
<grf:message xmlns:grf="http://www.company.com/schemas/device/version001">
<grf:messageHeader>
<grf:messageType>instrumentStatus</grf:messageType>
</grf:messageHeader>
<grf:messageBody>
<grf:instrumentStatusBody>
<grf:statusId>Running</grf:statusId>
</grf:instrumentStatusBody>
</grf:messageBody>
</grf:message>
There is a mapping for status_id integer values in the XML as follows:
status-id Meaning
========= =======
0 Ready
1 Running
2 NotReady
3 PoweringUp
4 PoweringDown
5 PoweredUp
6 PoweredDown
7 Tuning
8 Error
My XSLT is working correctly and giving me the correct output when I use Altova XMLSpy, but when I run my .NET application, I'm getting a failure at the point where the mapping for the status_id integer is converted to one of the allowable enumerated strings. Instead of getting the enumerated value, the MS XSLT processor returns an empty string and I get an empty <status_id/> element in the output XML.
The following is my XSLT code with some sections removed to reduce the amount of space:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:grf="http://www.company.com/schemas/device/version001"
exclude-result-prefixes="#default">
<xsl:template match="/">
<xsl:apply-templates select="message"/>
</xsl:template>
<xsl:template match="message">
<xsl:element name="grf:message">
<xsl:apply-templates select="/message/cmd/#id"/>
</xsl:element>
</xsl:template>
<xsl:template match="/message/cmd/#id">
<xsl:variable name="_commandType" select="/message/cmd/#id"/>
<!-- Following line works in Altova XMLSpy, but fails in .NET app. ??? -->
<xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>
<xsl:element name="grf:messageHeader">
<xsl:element name="grf:messageType">
<xsl:choose>
<xsl:when test="$_commandType = 'api_info'">
<xsl:text>apiInfo</xsl:text>
</xsl:when>
<xsl:when test="$_commandType = 'instrument_status'">
<xsl:text>instrumentStatus</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:element>
<xsl:element name="grf:messageBody">
<xsl:choose>
<xsl:when test="$_commandType = 'api_info'">
<xsl:element name="grf:apiInfoBody">
<xsl:element name="grf:apiVersion">
<xsl:value-of select="/message/cmd/api-version"/>
</xsl:element>
<xsl:element name="grf:apiBuild">
<xsl:value-of select="/message/cmd/api-build"/>
</xsl:element>
</xsl:element>
</xsl:when>
<xsl:when test="$_commandType = 'instrument_status'">
<xsl:element name="grf:instrumentStatusBody">
<xsl:element name="grf:statusId">
<xsl:choose>
<xsl:when test="$_statusIdValue = '0'">
<xsl:text>Ready</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '1'">
<xsl:text>Running</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '2'">
<xsl:text>NotReady</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '3'">
<xsl:text>PoweringUp</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '4'">
<xsl:text>PoweringDown</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '5'">
<xsl:text>PoweredUp</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '6'">
<xsl:text>PoweredDown</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '7'">
<xsl:text>Tuning</xsl:text>
</xsl:when>
<xsl:when test="$_statusIdValue = '8'">
<xsl:text>Error</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:element>
</xsl:when>
</xsl:choose>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Is there XSLT 1.0 code that will behave the same in both Altova XMLSpy and the MS XSLT processor?
Thanks,
AlarmTripper
One thing to note is that in the template that matches the "message" element, you do this
<xsl:apply-templates select="/message/cmd/#id"/>
This will actually try to match the very first message in the XML relative to the document root, regardless of what message you are currently on. It is not selecting relative to the current node. In your case, it looks like there will only ever be one message, so it won't be an issue here, but it would be in other cases.
It is also probably more common to match on elements, rather than attributes, especially where you want to process child elements of an element. So, you would probably replace the above line with this instead
<xsl:apply-templates select="cmd"/>
Then, for the template that matches it, instead of doing this currently
<xsl:template match="/message/cmd/#id">
You would do this instead
<xsl:template match="cmd">
Next, within this template, you could try replacing your variables with simpler select statements
<xsl:variable name="_commandType" select="#id"/>
<xsl:variable name="_statusIdValue" select="status_id"/>
See if that makes a difference.
You are way over-complicating your transformation. Try this considerably simpler stylesheet:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.company.com/schemas/device/version001"
>
<xsl:output indent="yes" encoding="utf-8" />
<!-- main template / entry point -->
<xsl:template match="message">
<message>
<messageHeader>
<xsl:apply-templates select="cmd" mode="head" />
</messageHeader>
<messageBody>
<xsl:apply-templates select="cmd" mode="body" />
</messageBody>
</message>
</xsl:template>
<!-- header templates -->
<xsl:template match="cmd[#id = 'api_info']" mode="head">
<messageType>apiInfo</messageType>
</xsl:template>
<xsl:template match="cmd[#id = 'instrument_status']" mode="head">
<messageType>instrumentStatus</messageType>
</xsl:template>
<!-- body templates -->
<xsl:template match="cmd[#id = 'api_info']" mode="body">
<apiInfoBody>
<apiVersion><xsl:value-of select="api-version" /></apiVersion>
<apiBuild><xsl:value-of select="api-build" /></apiBuild>
</apiInfoBody>
</xsl:template>
<xsl:template match="cmd[#id = 'instrument_status']" mode="body">
<instrumentStatusBody>
<statusId>
<xsl:choose>
<xsl:when test="status_id = 0">Ready</xsl:when>
<xsl:when test="status_id = 1">Running</xsl:when>
<xsl:when test="status_id = 2">NotReady</xsl:when>
<xsl:when test="status_id = 3">PoweringUp</xsl:when>
<xsl:when test="status_id = 4">PoweringDown</xsl:when>
<xsl:when test="status_id = 5">PoweredUp</xsl:when>
<xsl:when test="status_id = 6">PoweredDown</xsl:when>
<xsl:when test="status_id = 7">Tuning</xsl:when>
<xsl:when test="status_id = 8">Error</xsl:when>
<!-- just in caseā€¦ -->
<xsl:otherwise>
<xsl:text>Unknown status_id: </xsl:text>
<xsl:value-of select="status_id" />
</xsl:otherwise>
</xsl:choose>
</statusId>
</instrumentStatusBody>
</xsl:template>
</xsl:stylesheet>
I got rid of all your seemingly superfluous namespace definitions (add them back as you need them) and put your stylesheet into a default namespace. This means you don't need a 'grf:' prefix on every element anymore, without changing the actual result:
<?xml version="1.0" encoding="utf-8"?>
<message xmlns="http://www.company.com/schemas/device/version001">
<messageHeader>
<messageType>instrumentStatus</messageType>
</messageHeader>
<messageBody>
<instrumentStatusBody>
<statusId>Running</statusId>
</instrumentStatusBody>
</messageBody>
</message>
Note how I use different match expressions and different template modes to output the appropriate elements in the right situations. This way any <xsl:variable> or <xsl:choose> become unnecessary, making for a cleaner and more maintainable stylesheet.
Also, usually there is no need to define <xsl:element> explicitly, unless you want to output elements with dynamic names. In all other cases, you can write the element straight-away.
I'm sorry that I can't say for sure why your stylesheet does not run as intended. It works for me, and it looks okay(ish).
Do not hesitate to ask if any of the above is unclear.
It's been a long time since I've coded up any xslt's but based on what I'm seeing you might be able to change this line:
<xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>
to
<xsl:variable name="_statusIdValue" select="/message/cmd/status_id/."/>
That should tell it to select the content of the element vs the node itself.
Kind of like when you do a value-of operation and you want the node's text content. you would do the same thing.
For example if you wanted to spit back out the status id number you could use the following:
<xsl:value-of select="/message/cmd/status_id/."/>
OK, I found out that if I use the following line to assign the _statusIdValue variable, then the code will function correctly with the XslCompiledTransform class:
<xsl:variable name="_statusIdValue" select="msxsl:node-set(/message/cmd/*)/text()"/>
This replaces the original line which was:
<xsl:variable name="_statusIdValue" select="/message/cmd/status_id"/>
However, the assignment that works in for the XslCompiledTransform class doesn't work with Altova XMLSpy. Is there a variant of the assignment that will work correctly in both the Altova XMLSpy editor and with the XslCompiledTransform class?
Thanks,
AlarmTripper
This is such terrible coding I'm actually glad it doesn't work in .NET, I suggest you rewrite your stylesheet.
Try this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:grf="http://www.company.com/schemas/device/version001">
<xsl:template match="message">
<xsl:variable name="commmand-type" select="cmd/#id"/>
<xsl:variable name="status-id" select="cmd/status_id/text()"/>
<grf:message>
<grf:messageHeader>
<grf:messageType>
<xsl:choose>
<xsl:when test="$commmand-type = 'api_info'">apiInfo</xsl:when>
<xsl:when test="$commmand-type = 'instrument_status'">instrumentStatus</xsl:when>
</xsl:choose>
</grf:messageType>
</grf:messageHeader>
<grf:messageBody>
<xsl:choose>
<xsl:when test="$commmand-type = 'api_info'">
<grf:apiInfoBody>
<grf:apiVersion>
<xsl:value-of select="cmd/api-version"/>
</grf:apiVersion>
</grf:apiInfoBody>
<grf:apiBuild>
<xsl:value-of select="cmd/api-build"/>
</grf:apiBuild>
</xsl:when>
<xsl:when test="$commmand-type = 'instrument_status'">
<grf:instrumentStatusBody>
<grf:statusId>
<xsl:choose>
<xsl:when test="$status-id = '0'">Ready</xsl:when>
<xsl:when test="$status-id = '1'">Running</xsl:when>
<xsl:when test="$status-id = '2'">NotReady</xsl:when>
<xsl:when test="$status-id = '3'">PoweringUp</xsl:when>
<xsl:when test="$status-id = '4'">PoweringDown</xsl:when>
<xsl:when test="$status-id = '5'">PoweredUp</xsl:when>
<xsl:when test="$status-id = '6'">PoweredDown</xsl:when>
<xsl:when test="$status-id = '7'">Tuning</xsl:when>
<xsl:when test="$status-id = '8'">Error</xsl:when>
</xsl:choose>
</grf:statusId>
</grf:instrumentStatusBody>
</xsl:when>
</xsl:choose>
</grf:messageBody>
</grf:message>
</xsl:template>
</xsl:stylesheet>
I'm using only one <xsl:template>, you can refactor if you feel it's appropiate.