I have a XSL file in which I am creating a field like this:
<ServiceText>
<xsl:value-of select="concat(Yrs,'-',Mos,'-',Days,'-',Hrs)" />
</ServiceText>
The values of 'Yrs,'-',Mos,'-',Days,'-',Hrs , I am receiving from a Web service response and assiging it to the XSL directly. I cannot do any modification to the data in code for these fields, because that is how the ocde is. All data manipulation is on the xslt.
I want to do a data filtering on xslt as follows:
if value of yrs =-1 then yrs=""
if value of mos =-1 then mos=""
if value of Days =-1 then Days=""
if value of Hrs =-1 then Hrs=""
How can I do it on the XSL file?
XSLT 2.0:
<xsl:template match="/root">
<ServiceText>
<xsl:value-of select="string-join((Yrs, Mos, Days, Hrs)[.!=-1],'-')" />
</ServiceText>
</xsl:template>
See link for a Working example
In XSLT 1.0 use something like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*/*[not(.= -1)]">
<xsl:value-of select="concat(., substring('-', 2 - not(self::Hrs)))"/>
</xsl:template>
<xsl:template match="*/*[. = -1]"/>
</xsl:stylesheet>
When this transformation is applied on the following XML document:
<t>
<Yrs>-1</Yrs>
<Mos>7</Mos>
<Days>15</Days>
<Hrs>3</Hrs>
</t>
the wanted result is produced:
7-15-3
Do Note:
It seems that there is an assumption that the "-1" values form a contiguous group (right to left in the sequence Yrs, Mos,Days).
If this assumption is violated, it would be impossible to understand what is the missing part in 2013-10-8 -- is it the months or the days ?
Related
I am getting below output from database and based on that I have to group by the data based on composite_name and RN elements.
Output coming from Database -
<SelectFlowIdDetailsFromSOAInfraOutputCollection>
<SelectFlowIdDetailsFromSOAInfraOutput>
<FLOW_ID>200239</FLOW_ID>
<COMPOSITE_NAME>ABC</COMPOSITE_NAME>
<rn>1</rn>
</SelectFlowIdDetailsFromSOAInfraOutput>
-<SelectFlowIdDetailsFromSOAInfraOutput>
<FLOW_ID>200247</FLOW_ID>
<COMPOSITE_NAME>ABC</COMPOSITE_NAME>
<rn>1</rn>
</SelectFlowIdDetailsFromSOAInfraOutput>
<SelectFlowIdDetailsFromSOAInfraOutput>
<FLOW_ID>200301</FLOW_ID>
<COMPOSITE_NAME>GHI</COMPOSITE_NAME>
<rn>1</rn>
</SelectFlowIdDetailsFromSOAInfraOutput>
<SelectFlowIdDetailsFromSOAInfraOutput>
<FLOW_ID>200300</FLOW_ID>
<COMPOSITE_NAME>GHI</COMPOSITE_NAME>
<rn>1</rn>
</SelectFlowIdDetailsFromSOAInfraOutput>
</SelectFlowIdDetailsFromSOAInfraOutputCollection>
I have to perform group by on composite_name and rn so that expected output should be
<RRR>
<XYZ>
<COMPOSITE_NAME>abc</COMPOSITE_NAME>
FLOW_ID>200239,200247</FLOW_ID>
</XYZ>
<XYZ>
<COMPOSITE_NAME>GHI</COMPOSITE_NAME>
FLOW_ID>200300,200301</FLOW_ID>
</XYZ>
</RRR>
I have used below XSLT but not getting expected output.
<xsl:for-each-group select="/ns0:SelectFlowIdDetailsFromSOAInfraOutputCollection/ns0:SelectFlowIdDetailsFromSOAInfraOutput" group-by="concat(ns0:COMPOSITE_NAME ,'|', ns0:rn)">
<tns:EmailBody>
<tns:InterfaceName>
<xsl:value-of select="ns0:COMPOSITE_NAME"/>
</tns:InterfaceName>
<tns:FaultedId>
<xsl:for-each select="current-group">
<xsl:value-of select="ns0:FLOW_ID"/>
<xsl:if test="not(position() = last())">,</xsl:if>
</xsl:for-each>
</tns:FaultedId>
</tns:EmailBody>
</xsl:for-each-group>
Please help me with correct logic to achieve the output.
Thank you !
Your attempt makes very little sense. It looks like you copied snippets from various sources and thrown them together in random order hoping they will somehow combine themselves into a functioning code. This might work in a Harry Potter film, not in programming.
Try a straightforward textbook approach instead:
XSLT 2.0
<xsl:stylesheet version="2.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="/SelectFlowIdDetailsFromSOAInfraOutputCollection">
<RRR>
<xsl:for-each-group select="SelectFlowIdDetailsFromSOAInfraOutput" group-by="concat(COMPOSITE_NAME ,'|', rn)">
<XYZ>
<COMPOSITE_NAME>
<xsl:value-of select="COMPOSITE_NAME"/>
</COMPOSITE_NAME>
<FLOW_ID>
<xsl:value-of select="current-group()/FLOW_ID" separator=","/>
</FLOW_ID>
</XYZ>
</xsl:for-each-group>
</RRR>
</xsl:template>
</xsl:stylesheet>
Demo: https://xsltfiddle.liberty-development.net/3MEdvgZ
I am calling this XSL from Apache Camel and I set the header of the message to the parameter, but still I don't get the result.
I want an XSLT which gives me the sum based on a parameter the problem is that the parameter could be multiple values or one value
<EmpJob>
<EmpJob>
<userId>testID</userId>
<EmpPayCompRecurring>
<payComponent>1010</payComponent>
<endDate>2020-06-30T00:00:00.000</endDate>
<paycompvalue>3025.67</paycompvalue>
<userId>testID</userId>
<currencyCode>EUR</currencyCode>
<startDate>2020-06-01T00:00:00.000</startDate>
</EmpPayCompRecurring>
<EmpPayCompRecurring>
<payComponent>6097</payComponent>
<endDate>2019-12-31T00:00:00.000</endDate>
<paycompvalue>100.0</paycompvalue>
<userId>testID</userId>
<currencyCode>EUR</currencyCode>
<startDate>2018-12-06T00:00:00.000</startDate>
</EmpPayCompRecurring>
</EmpJob>
</EmpJob>
I created an XSLT transformation
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name = "custReturnDate" />
<xsl:template match="/EmpJob">
<xsl:variable name="apos">'</xsl:variable>
<xsl:variable name="payrecur">'1010','6097' </xsl:variable>
<mainroot>
<xsl:for-each select="EmpJob">
<root>
<__metadata>
<uri><xsl:value-of select="concat('EmpCompensation(seqNumber=1L,startDate=datetime',$apos ,$custReturnDate, $apos,',userId=',$apos,userId,$apos,')')"/></uri>
</__metadata>
<customDouble13><xsl:value-of select="sum(EmpPayCompRecurring[$payrecur]/paycompvalue) "/></customDouble13>
</root>
</xsl:for-each>
</mainroot>
</xsl:template>
</xsl:stylesheet>
"payrecur" should be the parameter later on according to this parameter I should sum node values.
P.S. I can change the parameter to anything because I am calling the template from code.
Given XSLT 2 or 3, I would declare the parameter as a sequence of strings or integers and then compare sum(EmpPayCompRecurring[payComponent = $payrecur]/paycompvalue).
So declare e.g <xsl:param name="payrecur" as="xs:string*" select="'1010','6097'"/>.
A minimal stylesheet would be
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:param name = "custReturnDate" />
<xsl:param name="payrecur" as="xs:string*" select="'1010','6097'"/>
<xsl:output indent="yes"/>
<xsl:template match="/EmpJob">
<xsl:variable name="apos">'</xsl:variable>
<mainroot>
<xsl:for-each select="EmpJob">
<root>
<__metadata>
<uri><xsl:value-of select="concat('EmpCompensation(seqNumber=1L,startDate=datetime',$apos ,$custReturnDate, $apos,',userId=',$apos,userId,$apos,')')"/></uri>
</__metadata>
<customDouble13>
<xsl:value-of select="sum(EmpPayCompRecurring[payComponent = $payrecur]/paycompvalue)"/>
</customDouble13>
</root>
</xsl:for-each>
</mainroot>
</xsl:template>
</xsl:stylesheet>
At https://xsltfiddle.liberty-development.net/bEzkTcM I get <customDouble13>3125.67</customDouble13>. You will probably want to add exclude-result-prefixes="#all" on the xsl:stylesheet element.
Hello everyone in case anyone faces this type of scenario as I mentiond before I added the parameter in the header so that the XSLT template will get it. Unfortunatly the template received it as a String so I had to use the function tokenize() to convert the parameter to array then I could use the template provided by Martin Honnen.
This scenario will work Apache Camel as well as SAP CPI
Best Regards
Ibrahim
I want to use if in xsl to compare two values, but i want to compare a string with a value that i can access it by xsl tag. if i compare with a value(without xsl tag), it works. I mean:
<xsl:if test="$prev4 >0">
but the fallowing has error:
<xsl:if test="$prev4 >0<xsl:value-of select="#wholeCount"/>">
I don't know how to do this comparing. Can you please help me? thank you
Actually, it isn't necessary at all to place the second argument in a variable -- so the currently accepted answer teaches you wrong.
Just use (both in XSLT 1.0 and XSLT 2.0):
<xsl:if test="$prev4 > #wholecounter">
Here is a complete example:
This simple XML document:
<t wholecounter="1"/>
when processed with the following transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:variable name="prev4" select="3"/>
<xsl:template match="/*">
<xsl:if test="$prev4 > #wholecounter">
$prev4 is greater than #wholecounter
</xsl:if>
</xsl:template>
</xsl:stylesheet>
the wanted, correct result is produced:
$prev4 is greater than #wholecounter
Define an XSL variable as below:
<xsl:variable name="counter" select="#wholeCount"/>
Then use the variable in comparison:
<xsl:if test="$prev4 > $counter">
I'm going to be brief. I'm doing XSLT on the client. The output is a report/html with data. The report consists of several blocks, ie one block is one child element of root-node in the xml file.
There are n reports residing in n different xslt-files in my project and the reports can have the same block. That means if there is a problem with one block for one report and it is in n reports i have to update every n report (xslt file).
So i want to put all my blocks in templates (kind-of-a businesslayer) that i can reuse for my reports by xsl:include on the templates for those reports.
So the pseudo is something like this:
<?xml version="1.0".....?>
<xsl:stylesheet version="1.0"....>
<xsl:include href="../../Blocks/MyBlock.xslt"/>
<xsl:template match='/'>
<xsl:apply-templates />
</xsl:template>
</xsl:stylesheet>
MyBlock.xslt:
<?xml version="1.0"....?>
<xsl:stylesheet version="1.0".....>
<xsl:template match='/root/rating'>
HTML OUTPUT
</xsl:template>
</xsl:stylesheet>
I hope someone out there understands my question. I need pointers on how to go about this, if this is one way to do it. But it doesn't seem to work.
Below is my experience that how am dealing this.
This is example which I modified your code.
<?xml version="1.0"?>
<xsl:stylesheet version="1.0">
<xsl:include href="../../Blocks/MyBlock.xslt"/>
<xsl:template match="/">
<xsl:apply-templates select="node()" mode="callingNode1"/>
</xsl:template>
</xsl:stylesheet>
MyBlock.xslt:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0">
<xsl:template mode="callingNode1" match="*">
HTML OUTPUT
</xsl:template>
<xsl:template mode="callingNode2" match="/root/rating">
HTML OUTPUT
</xsl:template>
</xsl:stylesheet>
Here am calling the nodes based on the mode & match.
Consider the following XSLT script:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="iso-8859-1"/>
<xsl:variable name="stringmap">
<map>
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</map>
</xsl:variable>
<xsl:template match="/">
<!-- IMPLEMENT ME -->
</xsl:template>
</xsl:stylesheet>
I'd like this script to print redgreenblue.
Is there any way to treat the XML markup which is stored in the stringmap variable as a document of its own which I can run XPath queries on? I'm basically looking for something like
<xsl:for-each select="document($stringmap)/map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
(except that the document() function expects an URI).
Motivation: I have various long <xsl:choose> elements which map a given string to another string. I'd like to replace all those with a single template which takes a 'map' argument (which is a simple XML document). My hope is that I can then replace the <xsl:choose> with a simple statement like <xsl:value-of select="$stringmap/map/entry/value[../key='$givenkey']"/>
I'm using XSLT 1.0 using xsltproc.
You're almost right, using document('') will allow you to process node sets inside the current stylesheet:
<xsl:for-each select="document('')/xsl:stylesheet/xsl:variable[#name='stringmap']/map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
It's not necessary to define the map node set as a variable in this case:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:data="some.uri" version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<data:map>
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</data:map>
<xsl:template match="/">
<xsl:for-each select="document('')/xsl:stylesheet/data:map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
If you do not use xsl:variable as a wrapper, you must remember that a top level elements must have a non null namespace URI.
In XSLT 2.0 it would've been possible to just iterate over the content in a variable:
<xsl:variable name="map">
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</xsl:variable>
<xsl:template match="/">
<xsl:for-each select="$map/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
</xsl:template>
A posting by M. David Peterson just taught me how to make this work:
It's not necessary to have an <xsl:variable> for this case. Instead, I can embed the data document directly into the XSL stylesheet (putting it into a namespace for sanity) and then select elements from that. Here's the result:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:map="uri:map">
<xsl:output method="text" encoding="iso-8859-1"/>
<map:colors>
<entry><key>red</key><value>rot</value></entry>
<entry><key>green</key><value>gruen</value></entry>
<entry><key>blue</key><value>blau</value></entry>
</map:colors>
<xsl:template match="/">
<xsl:for-each select="document('')/*/map:colors/entry">
<xsl:value-of select="key"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This generates the expected output redgreenblue.
The trick is to use document('') to get a handle to the XSLT document itself, then * to get into the toplevel xsl:stylesheet element and from there I can access the color map.