I asked a similar question earlier. I have made some changes to the code, but am still stuck.
I have part of an XSLT like this:
<xsl:variable name="y" select="0" />
<xsl:if test="units_display='true'">
<xsl:call-template name="DisplayBox">
<xsl:with-param name="current_y" select="$y" />
<xsl:with-param name="value" select="units" />
<xsl:with-param name="text" select="'Units'" />
</xsl:call-template>
</xsl:if>
<xsl:if test="sensor_display='true'">
<xsl:call-template name="DisplayBox">
<xsl:with-param name="current_y" select="$y" />
<xsl:with-param name="value" select="sensor" />
<xsl:with-param name="text" select="'Type'" />
</xsl:call-template>
</xsl:if>
<xsl:if test="offset_display='true'">
<xsl:call-template name="DisplayBox">
<xsl:with-param name="current_y" select="$y" />
<xsl:with-param name="value" select="offset" />
<xsl:with-param name="text" select="'Offset'" />
</xsl:call-template>
</xsl:if>
My call-template is like this:
<xsl:template name="DisplayBox">
<xsl:param name="current_y" />
<xsl:param name="value" />
<xsl:param name="text" />
<rect x="20" y="{150 + $current_y}" width="220" height="20" fill="#FFFFFF" stroke="black" stroke-width="1" />
<text x="25" y="{168 + $current_y}" font-family="arial" font-size="20px" fill="black">
<xsl:value-of select="$value"/>
</text>
<line x1="90" y1="{150 + $current_y}" x2="90" y2="{170 + $current_y}" stroke="black" stroke-width="1" />
<text x="95" y="{168 + $current_y}" font-family="arial" font-size="20px" fill="black"><xsl:value-of select="$text" /></text>
</xsl:template>
I can't work out how to increase the value of current_y depending on whether the if statements are true or not. E,g, if a statement is true, the y value needs to be increased by 20, but not if the statement is false.
So the output could be empty if all 3 statements are false or could be any of these permutations:
Any help would be greatly appreciated.
You have to move the whole thing into a recursion template. You are passing $y for "current_y" which has the value "0" into each call of the template. That recursive template would take the same arguments and two additional:
(1) The test to perform for that round of recursion
(2) Whether you have any additional tests to perform (or exit the recursion)
Then you do a choose and increment the "y" (or not) depending on the test.
* New Info *
After some more thought based on the comments below, what about using something like this:
<xsl:template match="units_display | sensor_display | offset_display">
<xsl:variable name="y-multiplier">
<xsl:value-of select="count(preceding-sibling::units_display[.='true']) + count(preceding-sibling::sensor_display[.='true']) + count(preceding-sibling::offset_display[.='true'])"/>
</xsl:variable>
<xsl:message>
<xsl:value-of select="$y-multiplier"/>
</xsl:message>
</xsl:template>
This counts all of the elements that meet you criteria as you go through the document. This should get you started toward writing the result without recursion.
Related
My input xml is this
<StepSchedule>
<StepRateEffectiveDate>20191204</StepRateEffectiveDate>
<StepRate>3.400000</StepRate>
</StepSchedule>
<StepSchedule>
<StepRateEffectiveDate>20200304</StepRateEffectiveDate>
<StepRate>2.250000</StepRate>
</StepSchedule>
<StepSchedule>
<StepRateEffectiveDate>20200604</StepRateEffectiveDate>
<StepRate>2.000000</StepRate>
</StepSchedule>
<StepSchedule>
<StepRateEffectiveDate>20201204</StepRateEffectiveDate>
<StepRate>1.510000</StepRate>
</StepSchedule>
My xslt where I need to reformat the dates to a different format
<xsl:call-template name="date-formatter">
<xsl:with-param name="date" select="./*:StepSchedule/*:StepRateEffectiveDate" />
</xsl:call-template>
<xsl:template name="date-formatter">
<xsl:param name="date" />
<xsl:variable name="year" select="substring($date, 1, 4)" />
<xsl:variable name="month" select="substring($date, 5, 2)" />
<xsl:variable name="day" select="substring($date, 7, 2)" />
<xsl:value-of select="concat($year,'-',$month,'-',$day,'T00:00:00')" />
</xsl:template>
When I run the transformation I get the following error
Transform terminated: 'Type check error. In call to built-in function 'substring', expected type '{http://www.w3.org/2001/XMLSchema}string?' for argument 1 ('$sourceString' in namespace '') but found more than one item.
When the input only contains one StepSchedule node it works fine, the problem is when there is more than one.
The context is not really clear, but if you replace
<xsl:call-template name="date-formatter">
<xsl:with-param name="date" select="./*:StepSchedule/*:StepRateEffectiveDate" />
</xsl:call-template>
by
<xsl:template match="/">
<xsl:for-each select="//StepRateEffectiveDate">
<xsl:call-template name="date-formatter">
<xsl:with-param name="date" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
it seems to work. I'm not sure what your XPath is trying to select.
See it working here: https://xsltfiddle.liberty-development.net/bET2rWP
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 8 years ago.
Improve this question
Problem in the cases in the below XSLt code, even though variables are defined, it shows undefined variable.
First choose is to test wheather the Year is for Leap year or non Leap Year.
Second choose only finds the cases for number of Days of the month.
<xsl:template name="CalDateNTime">
<xsl:param name="pDate"/>
<xsl:param name="pMonth"/>
<xsl:param name="pYear"/>
<xsl:choose>
<!-- Leap Year -->
<xsl:when test="($pYear mod 4=0 and $pYear mod 100 !=0) or $pYear mod 400 =0" >
<xsl:value-of select="$pDate"/>
<xsl:value-of select="$rDate"/>
<xsl:value-of select="$pMonth"/>
<xsl:choose>
<xsl:when test="$pMonth=1 and $rDate=32">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="2"/>
</xsl:when>
<xsl:when test="$pMonth=2 and $rDate=30">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="3"/>
</xsl:when>
<xsl:when test="$pMonth=3 and $rDate=32">
<xsl:variable name="rDate" select="1"/>
<xsl:variable name="rMonth" select="4"/>
</xsl:when>
</xsl:choose>
</xsl:when>
<!-- Non - Leap Year -->
<xsl:otherwise>
<xsl:variable name="jan" select="31"/>
<xsl:variable name="feb" select="28"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
XSLT variables only exist within their parent.
<xsl:when test="$pMonth=1 and $rDate=32">
<xsl:variable name="rDate" select="1" />
<xsl:variable name="rMonth" select="2" />
</xsl:when>
These two variables are out of scope immediately (at the </xsl:when>).
You must structure your program differently. Put the <xsl:choose> into the variable.
I can only assume what your partial sample code is supposed to do. My assumption is: It should calculate the day after a given date.
Here's an alternative implementation:
<xsl:template name="CalDateNTime">
<xsl:param name="pDate" />
<xsl:param name="pMonth" />
<xsl:param name="pYear" />
<xsl:variable name="dayNum" select="'312831303130313130313031'" />
<xsl:variable name="isLeap" select="($pYear mod 4 = 0 and $pYear mod 100 != 0) or ($pYear mod 400 = 0)" />
<!-- determine index into the $dayNum string -->
<xsl:variable name="idx" select="($pMonth - 1) * 2" />
<xsl:variable name="maxDate" select="substring($dayNum, $idx + 1, 2)" />
<xsl:variable name="isSameMonth" select="($pDate < $maxDate) or ($pDate = $maxDate and $pMonth = 2 and $isLeap)" />
<!-- calculate following day, month, year values -->
<xsl:variable name="nDate">
<xsl:choose>
<xsl:when test="$isSameMonth"><xsl:value-of select="$pDate + 1" /></xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nMonth">
<xsl:choose>
<xsl:when test="$isSameMonth"><xsl:value-of select="$pMonth" /></xsl:when>
<xsl:when test="$pMonth < 12"><xsl:value-of select="$pMonth + 1" /></xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="nYear">
<xsl:choose>
<xsl:when test="not($isSameMonth) and $nMonth = 1"><xsl:value-of select="$pYear + 1" /></xsl:when>
<xsl:otherwise><xsl:value-of select="$pYear" /></xsl:otherwise>
</xsl:choose>
</xsl:variable>
<!-- build output string -->
<xsl:value-of select="concat($nYear, '-')" />
<xsl:if test="$nMonth < 10">0</xsl:if>
<xsl:value-of select="concat($nMonth, '-')" />
<xsl:if test="$nDate < 10">0</xsl:if>
<xsl:value-of select="$nDate" />
</xsl:template>
Usage
<xsl:call-template name="CalDateNTime">
<xsl:with-param name="pDate" select="28" />
<xsl:with-param name="pMonth" select="2" />
<xsl:with-param name="pYear" select="2000" />
</xsl:call-template>
Result
2000-02-29
<xsl:value-of select="IPADDRESS" />
The above line returns the IP address 192.123.201.21 but I want the output to be 192.123.201. How to split the string at . and remove the last token?
In XSLT 1.0 you need to work a little harder at it:
<xsl:variable name="lastOctet" select="substring-after(substring-after(substring-after(IPADDRESS, '.'), '.'), '.')" />
<xsl:value-of select="substring(IPADDRESS, 1, string-length(IPADDRESS) - string-length($lastOctet) - 1)" />
The XPath 1.0 substring-before and substring-after functions can give you the substring before/after the first occurrence of a given separator, but to find the substring before the last occurrence you need to use a tail-recursive template
<xsl:template name="substring-before-last">
<xsl:param name="str" />
<xsl:param name="separator" />
<xsl:param name="prefix" select="''" /><!-- first segment - no prefix -->
<xsl:variable name="after-first" select="substring-after($str, $separator)" />
<xsl:if test="$after-first">
<xsl:value-of select="concat($prefix, substring-before($str, $separator))" />
<xsl:call-template name="substring-before-last">
<xsl:with-param name="str" select="$after-first" />
<xsl:with-param name="separator" select="$separator" />
<!-- for second and subsequent segments, prepend a $separator -->
<xsl:with-param name="prefix" select="$separator" />
</xsl:call-template>
</xsl:if>
</xsl:template>
This template keeps writing out segments between separators until it reaches a point where there are no more instances of the separator string. You would call it by replacing your xsl:value-of element with
<xsl:call-template name="substring-before-last">
<xsl:with-param name="str" select="IPADDRESS" />
<xsl:with-param name="separator" select="'.'" /><!-- note the single quotes -->
</xsl:call-template>
With XSLT 2.0 you could use <xsl:value-of select="tokenize(IPADDRESS, '\.')[position() lt last()]" separator="."/>.
This should work (referring to your title: "how to trim result string value?"):
<xsl:value-of select="substring(IPADDRESS,1,11)" />
Can you rely on the IPADDRESS element to always have the same structure and content? If so, there is no need to tokenize.
I have the following XSL file from the DCM4CHEE DICOM project and I was trying to adjust it slightly. The code I'm actually trying to get working is commented out, but even the variable assignment seems to actually be returning null. The DCM4CHEE logs are throwing Java exceptions with a 'null' seeming to be coming from the XSL template when it compiles it.
<xsl:call-template name="attr">
<xsl:with-param name="tag" select="'00100040'"/>
<xsl:with-param name="vr" select="'CS'"/>
<xsl:variable name="testing" select="string(field[8]/text())" />
<xsl:with-param name="val" select="$testing" />
<!--
<xsl:variable name="sexString" select="string(field[8]/text())" />
<xsl:variable name="sex">
<xsl:choose>
<xsl:when test="$sexString='1'">M</xsl:when>
<xsl:when test="$sexString='2'">F</xsl:when>
<xsl:when test="$sexString='9'">U</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$sexString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:with-param name="val" select="$sex" /> -->
</xsl:call-template>
The normal XSL is just one simple line:
<xsl:with-param name="val" select="string(field[8]/text())" />
I'm probably doing something very wrong, but can someone explain why I'm not able to assign field[8]/text() to a variable and then pass it to the with-param?
<xsl:call-template name="attr">
<xsl:with-param name="tag" select="'00100040'"/>
<xsl:with-param name="vr" select="'CS'"/>
<xsl:variable name="testing" select="string(field[8]/text())" />
<xsl:with-param name="val" select="$testing" />
</xsl:call-template>
I'm probably doing something very wrong, but can someone explain why
I'm not able to assign field[8]/text() to a variable and then pass it
to the with-param?
Yes, the code is so wrong that the XSLT processor should throw an error message without compiling/executing it.
According to the W3C XSLT 1.0 specification, the only allowed element as child of xsl:call-template is xsl:with-param.
The presented code clearly violates this syntactic rules by placing other elements (xsl:variable) as children of xsl:call-template).
Solution: Move the variables out (before) the xsl:call-template:
<xsl:variable name="testing" select="string(field[8]/text())" />
<xsl:call-template name="attr">
<xsl:with-param name="tag" select="'00100040'"/>
<xsl:with-param name="vr" select="'CS'"/>
<xsl:with-param name="val" select="$testing" />
</xsl:call-template>
The code above is syntactically correct.
Ok, I'm stumped. I would like test if a parameter sent to an XSLT template contains a period and to print out quotes if it does not. The parameter I would like to test is "value" in the template below. It seems the contains function should work, but for some reason the quotes always get outputted regardless the contents of "value". What am I doing wrong? Thanks
<!-- Add a JSON property -->
<xsl:template name="addProperty">
<xsl:param name="name" />
<xsl:param name="value" />
<xsl:value-of select="$name" />
<xsl:text>:</xsl:text>
<xsl:if test="not(contains($value,'.'))">'</xsl:if>
<xsl:value-of select="$value" />
<xsl:if test="not(contains($value,'.'))">'</xsl:if>
<xsl:text>,</xsl:text>
</xsl:template>
When I call your template, it works fine. How are you calling it? This is what I used:
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'abc'"/>
<xsl:with-param name="value" select="'123'"/><!-- quoted number -->
</xsl:call-template>
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'abc'"/>
<xsl:with-param name="value" select="123"/><!-- NOT quoted number -->
</xsl:call-template>
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'xyz'"/>
<xsl:with-param name="value" select="'456.789'"/><!-- quoted number -->
</xsl:call-template>
<xsl:call-template name="addProperty">
<xsl:with-param name="name" select="'xyz'"/>
<xsl:with-param name="value" select="456.789"/><!-- NOT quoted number -->
</xsl:call-template>
And this is what I got as output:
abc:'123',abc:'123',xyz:456.789,xyz:456.789,
Could you not be passing in the values to the named template that you think you are passing in? What XSLT engine are you using?
A good way to test this is to add something like this to your named template and see what it produces, if you don't have a good debugger handy:
XXX<xsl:value-of select="$value"/>XXX
YYY<xsl:value-of select="contains($value, '.')"/>YYY
ZZZ<xsl:value-of select="not(contains($value, '.'))"/>ZZZ