How to calculate time difference between two given time in xslt? - xslt

Given below is the xml.
I want to display time difference like: "2h 52m".
START TIME - 09:02
STOP TIME - 11:45
TOTAL -2h 52m
<Surgery>
<SURGERY_START_TIME>9:02</SURGERY_START_TIME>
<SURGERY_STOP_TIME>11:45</SURGERY_STOP_TIME>
</Surgery>
I tried the below code but that doesn't work,
<xsl:variable name="surgStartTime">
<xsl:value-of select="SURGERY_START_TIME/."/>
</xsl:variable>
<xsl:variable name="surgStopTime">
<xsl:value-of select="SURGERY_STOP_TIME/."/>
</xsl:variable>
<xsl:value-of select="$surgStartTime -$surgStopTime)/>

There is no support for date/time arithmetic in XSLT 1.0; you need to do the calculation yourself - e.g:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="Surgery">
<!-- calculate the duration in minutes -->
<xsl:variable name="duration" select="60 * substring-before(SURGERY_STOP_TIME, ':') + substring-after(SURGERY_STOP_TIME, ':') - 60 * substring-before(SURGERY_START_TIME, ':') - substring-after(SURGERY_START_TIME, ':')" />
<xsl:copy>
<xsl:copy-of select="*"/>
<DURATION>
<!-- output whole hours -->
<xsl:value-of select="floor($duration div 60)"/>
<xsl:text>h </xsl:text>
<!-- output remaining minutes -->
<xsl:value-of select="$duration mod 60"/>
<xsl:text>m</xsl:text>
</DURATION>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Applied to your input example, the result will be:
<?xml version="1.0" encoding="UTF-8"?>
<Surgery>
<SURGERY_START_TIME>9:02</SURGERY_START_TIME>
<SURGERY_STOP_TIME>11:45</SURGERY_STOP_TIME>
<DURATION>2h 43m</DURATION>
</Surgery>
and not 2h 52m as stated in your question.

Related

XSLT 1.0 - how to check when condition for string

I am trying to conditional check on the input xml file and place the value.
input xml:
<workorder>
<newwo>1</newwo>
</workorder>
If newwo is 1, then I have to set in my output as "NEW" else "OLD"
Expected output is:
newwo: "NEW"
my xslt is:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<xsl:template match="/">
<xsl:apply-templates select="NEWWO" />
</xsl:template>
<xsl:template match="/NEWWO">
<xsl:text>{
newwo:"
</xsl:text>
<xsl:choose>
<xsl:when test="NEWWO != '0'">NEW</xsl:when>
<xsl:otherwise>OLD</xsl:otherwise>
</xsl:choose>
<xsl:text>"
}</xsl:text>
</xsl:template>
Please help me. Thanks in advance!
I see a number of reasons you aren't getting output.
The xpaths are case sensitive. NEWWO is not going to match newwo.
You match / and then apply-templates to newwo (case fixed), but newwo doesn't exist at that context. You'll either have to add */ or workorder/ to the apply-templates (like select="*/newwo") or change / to /* or /workorder in the match.
You match /newwo (case fixed again), but newwo is not the root element. Remove the /.
You do the following test: test="newwo != '0'", but newwo is already the current context. Use . or normalize-space() instead. (If you use normalize-space(), be sure to test against a string. (Quote the 1.))
Here's an updated example.
XML Input
<workorder>
<newwo>1</newwo>
</workorder>
XSLT 1.0
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/*">
<xsl:apply-templates select="newwo" />
</xsl:template>
<xsl:template match="newwo">
<xsl:text>{
newwo: "</xsl:text>
<xsl:choose>
<xsl:when test=".=1">NEW</xsl:when>
<xsl:otherwise>OLD</xsl:otherwise>
</xsl:choose>
<xsl:text>"
}</xsl:text>
</xsl:template>
</xsl:stylesheet>
Output
{
newwo: "NEW"
}
You try it as below
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="1.0">
<xsl:template match="/">
<xsl:choose>
<xsl:when test="/workorder/newwo = 1">
<xsl:text disable-output-escaping="no"> newwo:New</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:text disable-output-escaping="no"> newwo:Old</xsl:text> </xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

number format for .csv using xslt1.0

I have tag in xml which value sometimes -ve number and sometimes +ve number and I want to show the -ve(e.g -10) number as (10) I mean with bracket using xslt1.0.
Could you please help me?
I am using below template but it is not working.
<xsl:call-template name="fmtNumber">
<xsl:with-param name="amt" select="retailer:Amount" />
</xsl:call-template>
<xsl:template name="fmtNumber">
<xsl:param name="amt" />
<xsl:value-of
select="format-number($amt, ' $###,##0 ;($###,##0)')" />
</xsl:template>
it is showing the result in csv file as $(10) but, I want (10), I mean without dollar sumbol.
do:
<xsl:value-of select="format-number($amt, '###,##0;(###,##0)')"/>
Please run the following stylesheet (with any XML source) on your processor and post the resulting code.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="num" select="-123" />
<xsl:template match="/">
<result>
<version><xsl:value-of select="system-property('xsl:version')"/></version>
<vendor><xsl:value-of select="system-property('xsl:vendor')"/></vendor>
<test><xsl:value-of select="format-number($num, '#,##0;(#,##0)')"/></test>
</result>
</xsl:template>
</xsl:stylesheet>

XSLT: Replace string with Abbreviations

I would like to know how to replace the string with the abbreviations.
My XML looks like below
<concept reltype="CONTAINS" name="Left Ventricular Major Axis Diastolic Dimension, 4-chamber view" type="NUM">
<code meaning="Left Ventricular Major Axis Diastolic Dimension, 4-chamber view" value="18074-5" schema="LN" />
<measurement value="5.7585187646">
<code value="cm" schema="UCUM" />
</measurement>
<content>
<concept reltype="HAS ACQ CONTEXT" name="Image Mode" type="CODE">
<code meaning="Image Mode" value="G-0373" schema="SRT" />
<code meaning="2D mode" value="G-03A2" schema="SRT" />
</concept>
</content>
</concept>
and I am selecting some value from the xml like,
<xsl:value-of select="concept/measurement/code/#value"/>
Now what I want is, I have to replace cm with centimeter. I have so many words like this. I would like to have a xml for abbreviations and replace from them.
I saw one similar example here.
Using a Map in XSL for expanding abbreviations
But it replaces node text, but I have text as attribute. Also, it would be better for me If I can find and replace when I select text using xsl:valueof select instead of having a separate xsl:template. Please help. I am new to xslt.
I have created XSLT v "1.1". For abbreviations I have created XML file as you have mentioned:
Abbreviation.xml:
<Abbreviations>
<Abbreviation>
<Short>cm</Short>
<Full>centimeter</Full>
</Abbreviation>
<Abbreviation>
<Short>m</Short>
<Full>meter</Full>
</Abbreviation>
</Abbreviations>
XSLT:
<xsl:stylesheet version="1.1" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes" method="xml" />
<xsl:param name="AbbreviationDoc" select="document('Abbreviation.xml')"/>
<xsl:template match="/">
<xsl:call-template name="Convert">
<xsl:with-param name="present" select="concept/measurement/code/#value"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="Convert">
<xsl:param name="present"/>
<xsl:choose>
<xsl:when test="$AbbreviationDoc/Abbreviations/Abbreviation[Short = $present]">
<xsl:value-of select="$AbbreviationDoc/Abbreviations/Abbreviation[Short = $present]/Full"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$present"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
INPUT:
as you have given <xsl:value-of select="concept/measurement/code/#value"/>
OUTPUT:
centimeter
You just need to enhance this Abbreviation.xml to keep short and full value of abbreviation and call 'Convert' template with passing current value to get desired output.
Here a little shorter version:
- with abbreviations in xslt file
- make use of apply-templates with mode to make usage shorter.
But with xslt 1.0 node-set extension is required.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="abbreviations_txt">
<abbreviation abbrev="cm" >centimeter</abbreviation>
<abbreviation abbrev="m" >meter</abbreviation>
</xsl:variable>
<xsl:variable name="abbreviations" select="exsl:node-set($abbreviations_txt)" />
<xsl:template match="/">
<xsl:apply-templates select="concept/measurement/code/#value" mode="abbrev_to_text"/>
</xsl:template>
<xsl:template match="* | #*" mode="abbrev_to_text">
<xsl:variable name="abbrev" select="." />
<xsl:variable name="long_text" select="$abbreviations//abbreviation[#abbrev = $abbrev]/text()" />
<xsl:value-of select="$long_text"/>
<xsl:if test="not ($long_text)">
<xsl:value-of select="$abbrev"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>

Update text in an element - Effectively

I need help from experts here to optimize the solution of updating string value in an element . I have this xml file as an input...
<?xml version="1.0" encoding="UTF-8"?>
<FullRequest>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<RequestDetail>
<!-- Request Element is a string of fixed length (50 characters) -->
<Request>THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG AGAIN!</Request>
<Request>THE TIME FOX JUMPED OVER THE LAZY DOG, PROGRESSED!</Request>
<Request>OWING TO THE WIDESPREAD KNOWLEDGE OF THE PHRASE AN</Request>
</RequestDetail>
</FullRequest>
Offset 5 in Request element will be unique and can be cross-referenced. Q, T and G are the IDs in the above request.
<?xml version="1.0" encoding="UTF-8"?>
<FullResponse>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<ResponseDetail>
<!-- Response element repeats for 3 times as indicated by the value of Looptimes -->
<!-- Id has a unique value -->
<Response>
<Id>Q</Id>
<Value1>ABC</Value1>
<Value2>XYZ</Value2>
<Value3>FGK</Value3>
</Response>
<Response>
<Id>T</Id>
<Value1>123</Value1>
<Value2>YOK</Value2>
<Value3>DSL</Value3>
</Response>
<Response>
<Id>G</Id>
<Value1>BAT</Value1>
<Value2>TKR</Value2>
<Value3>LAF</Value3>
</Response>
</ResponseDetail>
</FullResponse>
Taking the above xml, offset positions 10, 15 and 20 need to be replaced with values Value1, Value2 and Value3 respectively.
I have this XSL which does the job. Not sure how good this solution will work with few thousand records of 5000 characters each (50 characters in the Request element shown as an example here) with about 20 locations to edit.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:regexp="http://exslt.org/regular-expressions" exclude-result-prefixes="regexp">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:preserve-space elements="*"/>
<xsl:variable name="WebResponse" select="document('local:///ic1-data.xml')"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/FullRequest/RequestDetail/Request">
<xsl:variable name="currentLine" select="."/>
<xsl:variable name="id" select="substring($currentLine,5,1)"/>
<xsl:for-each select="$WebResponse/FullResponse/ResponseDetail/Response">
<xsl:variable name="ResId" select="Id"/>
<xsl:if test="$id = $ResId">
<xsl:element name="{name()}">
<!-- Update Value1 -->
<xsl:variable name="from" select="substring($currentLine,10,3)"/>
<xsl:variable name="UpdatedValue1"
select="regexp:replace($currentLine,$from,'',Value1)"/>
<!-- Update Value2 -->
<xsl:variable name="from2" select="substring($UpdatedValue1,15,3)"/>
<xsl:variable name="UpdatedValue2"
select="regexp:replace($UpdatedValue1,$from2,'',Value2)"/>
<!-- Update Value3 -->
<xsl:variable name="from3" select="substring($UpdatedValue2,20,3)"/>
<xsl:value-of select="regexp:replace($UpdatedValue2,$from3,'',Value3)"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The sample output will be as:
<?xml version="1.0" encoding="UTF-8"?>
<FullRequest>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<RequestDetail>
<Response>THE QUICKABCOWXYZOXFGKMPS OVER THE LAZY DOG AGAIN!</Response>
<Response>THE TIME 123 JYOKEDDSLER THE LAZY DOG, PROGRESSED!</Response>
<Response>OWING TO BAT WTKRSPLAFD KNOWLEDGE OF THE PHRASE AN</Response>
</RequestDetail>
</FullRequest>
I can only use XSLT 1.0
Can you suggest how to make this better?
Thanks.
Another more flexible approach would be:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:key name="kResponseById" match="Response" use="Id"/>
<xsl:variable name="WebResponse" select="document('ic1-data.xml')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Request/text()">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="$WebResponse">
<xsl:call-template name="replace">
<xsl:with-param name="pString" select="$vCurrent"/>
<xsl:with-param name="pValues"
select="key('kResponseById',
substring($vCurrent,5,1)
)/*[starts-with(local-name(),'Value')]"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="pString"/>
<xsl:param name="pValues"/>
<xsl:choose>
<xsl:when test="$pValues">
<xsl:variable name="pLimit"
select="substring-after(
local-name($pValues[1]),
'Value'
) * 5 + 4"/>
<xsl:call-template name="replace">
<xsl:with-param name="pString"
select="concat(
substring($pString, 1, $pLimit),
$pValues[1],
substring($pString, $pLimit + 4)
)"/>
<xsl:with-param name="pValues"
select="$pValues[position()!=1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output:
<FullRequest>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<RequestDetail>
<!-- Request Element is a string of fixed length (50 characters) -->
<Request>THE QUICKABCOWXYZOXFGKMPS OVER THE LAZY DOG AGAIN!</Request>
<Request>THE TIME 123 JYOKEDDSLER THE LAZY DOG, PROGRESSED!</Request>
<Request>OWING TO BAT WTKRSPLAFD KNOWLEDGE OF THE PHRASE AN</Request>
</RequestDetail>
</FullRequest>

Convert short form days of the week to day names in xslt

I have some short form day names like so:
M -> Monday
T -> Tuesday
W -> Wednesday
R -> Thursday
F -> Friday
S -> Saturday
U -> Sunday
How can I convert an xml element like <days>MRF</days> into the long version <long-days>Monday,Thursday,Friday</long-days> using xslt?
Update from comments
Days will not be repeated
This stylesheet
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:d="day"
exclude-result-prefixes="d">
<d:d l="M" n="Monday"/>
<d:d l="T" n="Tuesday"/>
<d:d l="W" n="Wednesday"/>
<d:d l="R" n="Thursday"/>
<d:d l="F" n="Friday"/>
<d:d l="S" n="Saturday"/>
<d:d l="U" n="Sunday"/>
<xsl:variable name="vDays" select="document('')/*/d:d"/>
<xsl:template match="days">
<long-days>
<xsl:apply-templates
select="$vDays[contains(current(),#l)]"/>
</long-days>
</xsl:template>
<xsl:template match="d:d">
<xsl:value-of select="#n"/>
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:template>
</xsl:stylesheet>
With this input:
<days>MRF</days>
Output:
<long-days>Monday,Thursday,Friday</long-days>
Edit: For those who wander, retaining the sequence order:
<xsl:variable name="vCurrent" select="current()"/>
<xsl:apply-templates
select="$vDays[contains($vCurrent,#l)]">
<xsl:sort select="substring-before($vCurrent,#l)"/>
</xsl:apply-templates>
Note: Because days wouldn't be repeated, this is the same as looking up for item existence in sequence with empty string separator.
That should do it... (There might be more elegant solutions though... ;-)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/days">
<long-days>
<xsl:if test="contains(.,'M')">Monday<xsl:if test="string-length(substring-before(.,'M'))=string-length(.)-1">,</xsl:if></xsl:if>
<xsl:if test="contains(.,'T')">Tuesday<xsl:if test="string-length(substring-before(.,'T'))=string-length(.)-1">,</xsl:if></xsl:if>
<xsl:if test="contains(.,'W')">Wednesday<xsl:if test="string-length(substring-before(.,'W'))=string-length(.)-1">,</xsl:if></xsl:if>
<xsl:if test="contains(.,'R')">Thursday<xsl:if test="string-length(substring-before(.,'R'))=string-length(.)-1">,</xsl:if></xsl:if>
<xsl:if test="contains(.,'F')">Friday<xsl:if test="string-length(substring-before(.,'F'))=string-length(.)-1">,</xsl:if></xsl:if>
<xsl:if test="contains(.,'S')">Saturday<xsl:if test="string-length(substring-before(.,'S'))=string-length(.)-1">,</xsl:if></xsl:if>
<xsl:if test="contains(.,'U')">Sunday<xsl:if test="string-length(substring-before(.,'U'))=string-length(.)-1">,</xsl:if></xsl:if>
</long-days>
</xsl:template>
The currently accepted solution always displays the long days names in chronological order and in addition, it doesn't display repeating (with same code) days.
Suppose we have the following XML document:
<days>STMSU</days>
I. This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="my" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<my:days>
<M>Monday</M>
<T>Tuesday</T>
<W>Wednesday</W>
<R>Thursday</R>
<F>Friday</F>
<S>Saturday</S>
<U>Sunday</U>
</my:days>
<xsl:key name="kLongByShort" match="my:days/*"
use="name()"/>
<xsl:variable name="vstylesheet"
select="document('')"/>
<xsl:template match="days">
<long-days>
<xsl:call-template name="expand"/>
</long-days>
</xsl:template>
<xsl:template name="expand">
<xsl:param name="pcodeString" select="."/>
<xsl:if test="$pcodeString">
<xsl:variable name="vchar" select=
"substring($pcodeString,1,1)"/>
<xsl:for-each select="$vstylesheet">
<xsl:value-of select=
"concat(key('kLongByShort',$vchar),
substring(',',1,string-length($pcodeString)-1)
)
"/>
</xsl:for-each>
<xsl:call-template name="expand">
<xsl:with-param name="pcodeString" select=
"substring($pcodeString,2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on the above document, produces the wanted, correct result:
<long-days>Saturday,Tuesday,Monday,Saturday,Sunday</long-days>
II. This XSLT 2.0 transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:variable name="vshortCodes" as="xs:integer+"
select="string-to-codepoints('MTWRFSU')"/>
<xsl:variable name="vlongDays" as="xs:string+"
select="'Monday','Tuesday','Wenesday','Thursday',
'Friday','Saturday','Sunday'
"/>
<xsl:template match="days">
<long-days>
<xsl:for-each select="string-to-codepoints(.)">
<xsl:value-of separator="" select=
"for $pos in position() ne last()
return
($vlongDays[index-of($vshortCodes,current())],
','[$pos])
"/>
</xsl:for-each>
</long-days>
</xsl:template>
</xsl:stylesheet>
when applied on the same XML document:
<days>STMSU</days>
produce the wanted, correct result:
<long-days>Saturday,Tuesday,Monday,Saturday,Sunday</long-days>