I am writing a function to split string without breaking word. For that i have return function which is doing recursive call to itself. It's giving me below error
When i am calling function with function prefix then getting error as "splitLine function under namespace http://whatever is not defined.".
When i am calling function without function prefix then getting Parse error in the function.
When i Try to use
<xsl:value-of select="fn:splitLine($inString,$length - 1)"/>
in otherwise condition of function get function not defined error.
When i try to use without function prefix :
<xsl:value-of select="fn:splitLine($inString,$length - 1)"/>
I get parse error in Function.
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://whatever">
<xsl:output omit-xml-declaration="no" />
<xsl:output method="xml" />
<xsl:function name="fn:splitLine" as="xs:string">
<xsl:param name="inString" as="xs:string"/>
<xsl:param name="length" as="xs:numeric"/>
<xsl:variable name="delimiters"> ,."!?()</xsl:variable>
<xsl:choose>
<xsl:when test="0.0 >= $length ">
<xsl:value-of select="$inString"/>
</xsl:when>
<xsl:when test="$length >= string-length($inString)">
<xsl:value-of select="$inString"/>
</xsl:when>
<xsl:when test="contains($delimiters,substring($inString,$length + 1,1))">
<xsl:value-of select="substring($inString,1,$length)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="fn:splitLine($inString,$length - 1)"/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
<xsl:template match="/">
<xsl:value-of select="fn:splitLine('3 ZHANLANGUAN RD XICHENG, , , BEIJING, , CN, ',35)"/>
</xsl:template>
</xsl:stylesheet>
I am expecting output to be "3 ZHANLANGUAN RD XICHENG, , ," without breaking word.
This must be a quirk of your XSLT processor.
I checked your XSLT-2.0 file with Saxon-HE 9.9.1.4J and it works as expected after adding the following namespace to the xsl:stylesheet element:
xmlns:xs="http://www.w3.org/2001/XMLSchema"
So, I guess, that your XSLT processor returned a different namespace as missing, because after adding the above one, all commands could be processed and the result is as expected.
Below is the result for the processor and Version : Processor : Oracle Corporation. : Version :2 –
Right. Oracle (before they acquired Sun) were well down the path to developing an XSLT 2.0 processor as part of the Oracle XDK (XML development kit). But they never really finished it, and it never achieved a high level of conformance with the spec.
It looks to me as if you have encountered a restriction in that processor - and I'm sure there are many others. I don't think many people are using the Oracle XDK these days, and I think it has had very little development since Oracle acquired Sun (and hence Java).
#Tim Thanks for the suggestion. Instead of using function , i Used named template and it worked perfectly fine :
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fn="http://whatever">
<xsl:output omit-xml-declaration="no" />
<xsl:output method="xml" />
<xsl:template name="splitLine">
<xsl:param name="inString"/>
<xsl:param name="length"/>
<xsl:variable name="delimiters"> ,."!?()</xsl:variable>
<xsl:choose>
<xsl:when test="0.0 >= $length ">
<xsl:value-of select="$inString"/>
</xsl:when>
<xsl:when test="$length >= string-length($inString)">
<xsl:value-of select="$inString"/>
</xsl:when>
<xsl:when test="contains($delimiters,substring($inString,$length + 1,1))">
<xsl:value-of select="substring($inString,1,$length)"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="splitLine">
<xsl:with-param name="inString" select="$inString" />
<xsl:with-param name="length" select="$length - 1" />
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="splitLine">
<xsl:with-param name="inString" select="'fullnameofsample supplier in the block" />
<xsl:with-param name="length" select="35" />
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>
Related
I am wondering how the following templates work in detail. Both return the same results. Both are recursive and both use substring() to split the string given by the calling template, but if I debug both templates, rec1 returns its string at once, while rec2 returns a value each time, it calls itself.
XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<foo style="äöb"></foo>
</root>
XSLT
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="#all">
<xsl:template match="foo">
<root>
<xsl:call-template name="rec1">
<xsl:with-param name="style" select="#style"/>
</xsl:call-template>
</root>
</xsl:template>
<xsl:template name="rec1">
<xsl:param name="style"/>
<xsl:variable name="firstChar" select="substring($style,1,1)"/>
<xsl:variable name="charMap">
<xsl:choose>
<xsl:when test="$firstChar='ä'">ae</xsl:when>
<xsl:when test="$firstChar='ö'">oe</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$firstChar"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="string-length($style) > 1">
<xsl:call-template name="rec1">
<xsl:with-param name="style" select="substring($style,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:variable>
<xsl:value-of select="$charMap"/>
</xsl:template>
<xsl:template name="rec2">
<xsl:param name="style"/>
<xsl:variable name="firstChar" select="substring($style,1,1)"/>
<xsl:choose>
<xsl:when test="$firstChar='ä'">ae</xsl:when>
<xsl:when test="$firstChar='ö'">oe</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$firstChar"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="string-length($style) > 1">
<xsl:call-template name="rec2">
<xsl:with-param name="style" select="substring($style,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
rec1 Template
rec1 recursively builds the value of the variable charMap and then outputs it. The only call to <xsl:value-of select="$charMap"/> that results in an output is the one coming from the root call of rec1, because all subsequent recursive calls happen inside the variable definition of charMap (<xsl:variable name=charMap>).
Actually, <xsl:variable name="charMap"> marks the beginning of the definition of the $charMap variable but for $charMap to be actually defined we need to reach </xsl:variable>… although before we reach it we enter a recursive call of rec1! All subsequent recursive calls will find themselves waiting for the next recursive call to finish before they can define their own shorter $charMap until we reach the last call.
In other words, for rec1 called with 'Sävsjö':
charMap0 = S + charMap1
charMap1 = ae + charMap2
charMap2 = v + charMap3
charMap3 = s + charMap4
charMap4 = j + charMap5
charMap5 = oe
As long as you don't know the value of charMap5, you can't have charMap0.
rec2 Template
rec2 has no charMap variable since each call outputs a partial result before doing the recursive call to rec2. In the case of rec2, no xsl:value-of happens inside a xsl:variable hence each xsl:value-of produces a direct output.
What is the best way to loop in XSLT from 1 to 60?
I research in net, there are some templates to do this, is there any other way for example like a built-in function?
In XSLT 2.0,
<xsl:for-each select="1 to 60">...</xsl:for-each>
But I guess that you must be using XSLT 1.0, otherwise you wouldn't be asking.
In XSLT 1.0 you should use recursion: a template that calls itself with a counter that's incremented on each call, and the recursion terminates when the required value is reached.
Alternatively there's a workaround in XSLT 1.0: provided your source document contains at least 60 nodes, you can do
<xsl:for-each select="(//node())[60 >= position()]">...</xsl:for-each>
The problem with simple recursion when processing long sequences is that often the space for the call stack becomes insufficient and the processing ends due to stack overflow. This typically happens with sequence length >= 1000.
A general technique to avoid this (implementable with any XSLT processor, even if it doesn't recognize tail-recursion) is DVC (Divide and Conquer) style recursion.
Here is an example of a transformation that successfully prints the numbers from 1 to 1000000 (1M):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="1"/>
<xsl:with-param name="pEnd" select="1000000"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="displayNumbers">
<xsl:param name="pStart"/>
<xsl:param name="pEnd"/>
<xsl:if test="not($pStart > $pEnd)">
<xsl:choose>
<xsl:when test="$pStart = $pEnd">
<xsl:value-of select="$pStart"/>
<xsl:text>
</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vMid" select=
"floor(($pStart + $pEnd) div 2)"/>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$pStart"/>
<xsl:with-param name="pEnd" select="$vMid"/>
</xsl:call-template>
<xsl:call-template name="displayNumbers">
<xsl:with-param name="pStart" select="$vMid+1"/>
<xsl:with-param name="pEnd" select="$pEnd"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When applied on any XML document (not used) this transformation produces the wanted result -- all the numbers from 1 to 1000000.
You can use/adapt this transformation for any task that needs to "do something N times".
Very simple check inside the foreach-loop
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
Based on Dimitre Novatchev's answer.
Example:
<xsl:variable name="maxItems" select="10" />
<xsl:variable name="sequence" select="any-sequence"/>
<xsl:for-each select="$sequence">
<!-- Maybe sort first -->
<xsl:sort select="#sort-by" order="descending" />
<!-- where the magic happens -->
<xsl:if test="$maxItems > position()">
do something
</xsl:if>
</xsl:for-each>
The basic example for V1.0 using recursion would it be like this:
<xsl:template match="/">
<Root>
<!-- Main Call to MyTemplate -->
<xsl:call-template name="MyTemplate" />
</Root>
</xsl:template>
<xsl:template name="MyTemplate">
<xsl:param name="index" select="1" />
<xsl:param name="maxValue" select="60" />
<MyCodeHere>
<xsl:value-of select="$index"/>
</MyCodeHere>
<!-- < represents "<" for html entities -->
<xsl:if test="$index < $maxValue">
<xsl:call-template name="MyTemplate">
<xsl:with-param name="index" select="$index + 1" />
<xsl:with-param name="total" select="$maxValue" />
</xsl:call-template>
</xsl:if>
</xsl:template>
XSLT works based on templates and you'll need a template do run that loop.
You'll need to build a template receiving start and end values and, inside it, make a recursive call computing with start + 1. When $start equals $end, you do return your template, without another call.
In practice: http://www.ibm.com/developerworks/xml/library/x-tiploop/index.html
I have a template with a parameter. How can I insert a tab character n times?
n is the value of the parameter.
In XSLT 2.0:
<xsl:for-each select="1 to $count"> </xsl:for-each>
(Sadly though, I suspect that if you were using XSLT 2.0 you wouldn't need to ask the question).
Another technique often used with XSLT 1.0 is the hack:
<xsl:for-each select="//*[position() <= $count]"> </xsl:for-each>
which works provided the number of elements in your source document is greater than the number of tab characters you want to output.
Just call it recursively; output a tab, then call the same template again with n-1 passed in, if n > 1.
<xsl:template name="repeat">
<xsl:param name="output" />
<xsl:param name="count" />
<xsl:if test="$count > 0">
<xsl:value-of select="$output" />
<xsl:call-template name="repeat">
<xsl:with-param name="output" select="$output" />
<xsl:with-param name="count" select="$count - 1" />
</xsl:call-template>
</xsl:if>
</xsl:template>
As has been pointed out, this example will actually output a minimum of one. In my experience where the output is whitespace, it's usually needed. You can adapt the principle of a recursive template like this any way you see fit.
This seems the simplest and most flexible to me.
For XSLT 1.0 (or perhaps 1.1).
<xsl:variable name="count">10</xsl:variable>
<xsl:variable name="repeat"><xsl:text> </xsl:text></xsl:variable>
<xsl:sequence select="string-join((for $i in 1 to $count return $repeat),'')"/>
Of course the count variable is where you assign your n parameter.
I used the variable repeat to hold the tab character, but you could just replace the $repeat with the tab character in single quotes in the sequence element. Note: This variable can be of a length greater than 1, which creates a whole bunch of possibilities.
It does not use recursion, so it won't run into a recursion limit.
I don't know the maximum value you can use for count, but I tested it up to 10,000.
Globally define a long enough array of tabs:
<xsl:variable name="TABS" select="' '" />
Then use like this:
<xsl:value-of select="fn:substring($TABS, 1, fn:number($COUNT))" />
(XSLT 1.0)
<xsl:template name="tabs">
<xsl:param name="n"/>
<xsl:if test="$n > 0"> <!-- When n = 0, output nothing. -->
<xsl:call-template name="tabs"> <!-- Recursive call: call same template... -->
<xsl:with-param name="n" select="$n - 1"/> <!-- ... for writing n - 1 tabs. -->
</xsl:call-template>
<xsl:text> </xsl:text> <!-- Add one tab character. -->
</xsl:if>
</xsl:template>
Example usage:
<xsl:call-template name="tabs">
<xsl:with-param name="n" select="3"/>
</xsl:call-template>
I've discovered an LGPL-licensed library for doing this called functx, as I was sure someone had to have already done this... This is a "standard library" type XSLT library, which contains a function called repeat-string. From the docs:
The functx:repeat-string function returns a string consisting of a given number of copies of $stringToRepeat concatenated together.
Where I use it like this in my code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:functx="http://www.functx.com">
<xsl:import href="../buildlib/functx-1.0.xsl"/>
<xsl:output omit-xml-declaration="yes" />
<xsl:variable name="INDENT" select="' '" />
....
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*" />
</xsl:copy>
</xsl:template>
<xsl:template match="data-pusher-properties">
<xsl:for-each select="property">
<xsl:choose>
...
<xsl:when test="boolean(#value = '${pusher.notifications.server}')">
<xsl:value-of select="functx:repeat-string($INDENT, #indent)" />
<xsl:text>"</xsl:text>
<xsl:value-of select="#name" />
<xsl:text>": </xsl:text>
<xsl:text>"</xsl:text>
<xsl:value-of select="$pusher.notifications.email.server" />
<xsl:text>"\
</xsl:text>
</xsl:when>
...
</xsl:choose>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
So for printing a tab character n times, call it like this:
<xsl:value-of select="functx:repeat-string(' ', n)" />
I know this question is old, but I hope this can still help someone.
Documentation for the repeat-string function
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.
How can we create a node set from values....
I have n numbers 1,2,3.......n.
I want to create a node set
<MYNMUMS>
<MYNUM>1</MYNUM>
<MYNUM>2</MYNUM>
<MYNUM>3</MYNUM>
<MYNUM>4</MYNUM>
....
<MYNUM>N</MYNUM>
</MYNMUMS>
As easy as that:
XSLT 1.0 solution:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<MyNums>
<xsl:call-template name="generateNumNodes">
<xsl:with-param name="pStart" select="1"/>
<xsl:with-param name="pEnd" select="10"/>
</xsl:call-template>
</MyNums>
</xsl:template>
<xsl:template name="generateNumNodes">
<xsl:param name="pStart"/>
<xsl:param name="pEnd"/>
<xsl:if test="$pEnd >= $pStart">
<xsl:variable name="vNumNodes"
select="$pStart -$pEnd+1"/>
<xsl:choose>
<xsl:when test="$vNumNodes = 1">
<MyNum><xsl:value-of select="$pStart"/></MyNum>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="vHalf" select=
"floor(($pStart+$pEnd) div 2)"/>
<xsl:call-template name="generateNumNodes">
<xsl:with-param name="pStart" select="$pStart"/>
<xsl:with-param name="pEnd" select="$vHalf"/>
</xsl:call-template>
<xsl:call-template name="generateNumNodes">
<xsl:with-param name="pStart" select="$vHalf+1"/>
<xsl:with-param name="pEnd" select="$pEnd"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on any XML document (not used), produces the desired output:
<MyNums>
<MyNum>1</MyNum>
<MyNum>2</MyNum>
<MyNum>3</MyNum>
<MyNum>4</MyNum>
<MyNum>5</MyNum>
<MyNum>6</MyNum>
<MyNum>7</MyNum>
<MyNum>8</MyNum>
<MyNum>9</MyNum>
<MyNum>10</MyNum>
</MyNums>
Do note the following:
The template generateNumNodes calls itself recursively.
This recursion is both time ( O(N) ), and space ( O(log2(N)) ) efficient and practically does overflow the stack -- no SO here!
The above feature is achieved by implementing the recursion in a DVC (DiVide and Conquer) style.
Unlike tail-recursion it will be successfully executed on any compliant XSLT processor.
The maximum recursion depth needed to generate 1000000 (one million numbers) is just 19.
XSLT 2.0 solution:
Even more elementary, no recursion, just using the XPath 2.0 to operator:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<MyNums>
<xsl:for-each select="1 to 10">
<MyNums>
<xsl:sequence select="."/>
</MyNums>
</xsl:for-each>
</MyNums>
</xsl:template>
</xsl:stylesheet>
XSLT is a transformation language. It is usually used when you already have some data in the form of an XML document, that you wish to transform into a different document (that may or may not be in XML format).
For the task of starting with "raw" data and generating an XML representation, XSLT is not well-suited.
I suggest you look into different langauges to solve this.