How to use XSLT with If condition and copy of elements - if-statement

I have an XSLT with
<xsl:copy-of select="ABCDEFG"/>
I tried applying If condition like below:
<xsl:if test="(string(STAT)='E0001') or (string(STAT)='I0002')">
<xsl:copy-of select="ABCDEFG"/>
</xsl:if>
I have multiple nodes: ABCDEFG in the XML and
System is currently not selecting any of the ABCDEFG nodes
with the above IF condition.
Please let me know how to write the If condition.
Also would like to know how to write If condition with 'NOT' instead
of multiple 'OR's
The Actual Input XML is
<ABCDEFG>
<STAT>E0001</STAT>
<PQRS>Z000002</PQRS>
</ABCDEFG>
<ABCDEFG>
<STAT>E0002</STAT>
<PQRS>Z000002</PQRS>
</ABCDEFG>
<ABCDEFG>
<STAT>I0002</STAT>
<PQRS>Z000002</PQRS>
</ABCDEFG>
The Output wanted after XSLT is
<ABCDEFG>
<STAT>E0001</STAT>
<PQRS>Z000002</PQRS>
</ABCDEFG>
<ABCDEFG>
<STAT>I0002</STAT>
<PQRS>Z000002</PQRS>
</ABCDEFG>

I am guessing (!) you want to do something like:
<xsl:copy-of select="ABCDEFG[STAT='E0001' or STAT='I0002']"/>

Related

How to append string values inside if condition

I am new to XSL.I have an XML as below, If CoverageCode equals -'HomeCoverage' then I have to verify for the next 3 elements of 'roofRestrictionEndt','sidingRestrictionEndt'and 'paintRestrictionEndt' . If 'roofRestrictionEndt' exits and its value is 'Y' then I need to print 'Roof' under the 'results' tag, If 'sidingRestrictionEndt' exists and its value is 'Y' then I need to print 'siding' in case if it exists along with the above one then I need to print 'Roof; siding'. If 'paintRestrictionEndt' exists and its value is 'Y' along with the other 2 elements then I need to print 'Roof; siding; paint'. I tried by declaring variables and wrote If conditions and tried to append values accordingly inside IF condition, but I came to know the declared variables are immutable. In java, we can achieve this by using StringBuffer. Is there any way to achieve this in XSL? Below is XML.
<locationCoverage ID="3">
<coverageCode >HomeCoverage</coverageCode>
<roofRestrictionEndt >Y</roofRestrictionEndt>
<sidingRestrictionEndt>Y</sidingRestrictionEndt>
<paintRestrictionEndt >Y</paintRestrictionEndt>
<locationCoverage>
Results should look like as below
<results>
<result>Roof;siding;paint</result>
</results>
If I have below input XML
<locationCoverage ID="3">
<coverageCode >HomeCoverage</coverageCode>
<roofRestrictionEndt >Y</roofRestrictionEndt>
<paintRestrictionEndt >Y</paintRestrictionEndt>
</locationCoverage>
For the above XML results should look like as below
<results>
<result>Roof;paint</result>
</results>
Appreciate it If anyone helps me with this. Thanks in advance.
If I understand this correctly (which is not at all certain), you want to do something like:
<xsl:template match="locationCoverage[coverageCode='HomeCoverage']">
<xsl:variable name="test-results">
<xsl:if test="roofRestrictionEndt='Y'">Roof </xsl:if>
<xsl:if test="sidingRestrictionEndt='Y'">siding </xsl:if>
<xsl:if test="paintRestrictionEndt='Y'">paint</xsl:if>
</xsl:variable>
<result>
<xsl:value-of select="translate(normalize-space($test-results), ' ', ';')"/>
</result>
</xsl:template>

In XSLT nested foreach loop always return first element

XLST receiving the date from apache-camel in below formate.
data format
<list>
<linked-hash-map>
<entry key="NAME">test1</entry>
</linked-hash-map>
<linked-hash-map>
<entry key="NAME">test2</entry>
</linked-hash-map>
</list>
My XSLT:
<xsl:stylesheet>
<xsl:template match="*">
<xsl:for-each select="//*[local-name()='linked-hash-map']">
<tag1>
<xsl:value-of select="string(//*[local-name()='entry'][#key='NAME'])"/>
</tag1t>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
OUTPUT always returns the first element.
<tag1>test1<tag1>
<tag1>test1<tag1>
What is wrong in above xslt and help generate xml with all elements.
Because path expressions starting with "//" select from the root of the document tree, you are selecting the same nodes every time in your xsl:value-of; and in XSLT 1.0, if you select multiple nodes, only the first one gets displayed.
Methinks you're using "//" because you've seen it in example code and don't actually understand what it means...
Within xsl:for-each, you normally want a relative path that selects from the node currently being processed by the for-each.
You've also probably picked up this *[local-name()='linked-hash-map'] habit from other people's code. With no namespaces involved, you can safely replace it with linked-hash-map.

Unable to split and merge in XSLT

I have an Xml
<input Inputxml="&ltOrder..<LinePPlineNO="1#quot;Line/> >" >
How do i remove some part of string using xsl .. for eg I need to remove a whole string from < to > for a PPline 1
I need to splie the string in 3 parts remove the string from lt to gt and merge the part 1na dpart 3 of string
<Test Attrib1="b" Attrib2="C" Inputxml="
<OrderLine OrderedQty="1" PrimeLineNo="1" ShipNode="ABC" $gt;
</OrderLine $gt;
<OrderLine OrderedQty="1" PrimeLineNo="2" ShipNode="ABC" $gt;
</OrderLine $gt;" />
For example I may have 100 Order lines but I need to find the one with Prime line 1 and remove it .. So if I have to remeove a line I have to remove from lt; to gt;
Your example is confusing. If you have an XML input such as:
<input Inputxml="<order><Line PPlineNO="1">Bingo</Line></order>"/>
where the Inputxml attribute holds the escaped XML:
<order><Line PPlineNO="1">Bingo</Line></order>
you can use:
<xsl:template match="input">
<result>
<xsl:value-of select="substring-before(substring-after(#Inputxml, 'PPlineNO="1">'), '</Line>')" />
</result>
</xsl:template>
to get:
<result>Bingo</result>
Note that is not a good way to parse XML (or rather what used to be XML). It would be much smarter to unescape it first, then parse it as XML. In XSLT 3.0, you can use the parse-xml() function for this. In XSLT 1.0/2.0, you can do:
<xsl:value-of select="#Inputxml" disable-output-escaping="yes"/>
save the result to a file, and process the resulting file using another XSLT stylesheet.

The number of inputs to the scripting functoid does not match with the number of inputs expected by the xsl:call template

I'm working on a BizTalk project and I need to get the minimum date between many dates which are in different nodes using BizTalk mapping and XSLT.
I have search for some example and I found this: How to find minimum date from different xml date nodes using xslt which seems relevant with my problem.
So I modified the code and put it in the Scripting functoid under script type "Inline XSLT Call Template" then I built my project and it returns me this error "The number of inputs to the scripting functoid does not match with the number of inputs expected by the xsl:call template."
As I am pretty newbie with XSLT and BizTalk, any help is welcome.
I have already move the link between Input node and functoid but got the same error.
Here is the XSLT Inline script
<xsl:template name="ExtractMinStartDate">
<xsl:for-each select="/*[local-name()='Employee']/*[local-name()='Contracts']/*[local-name()='Contract']/*[local-name()='EmployementStartDate']">
<xsl:sort select="substring(Date,1,4)" data-type="string" order="ascending"/>
<xsl:sort select="substring(Date,6,2)" data-type="string" order="ascending"/>
<xsl:sort select="substring(Date,9,2)" data-type="string" order="ascending"/>
<xsl:if test="position()=1">
<xsl:value-of select="EmployementStartDate" />
</xsl:if>
</xsl:for-each>
</xsl:template>
Here is the xml in input
<Employee xmlns="http://Securitas.ESB.HR.EmployeeUDMToRostar.MDSRequest_Schema">
<Number>157989</Number>
<SagaNumber>23776</SagaNumber>
<SurNameFull>Test</SurNameFull>
<FirstName>test</FirstName>
<GivenName>test</GivenName>
<Gender>Man</Gender>
<DateOfBirth>1992-04-07T00:00:00.0000000+02:00</DateOfBirth>
<EmailPrivate>test#hotmail.com</EmailPrivate>
<PhoneMobilePrivate>5145454558</PhoneMobilePrivate>
<PhonePrivate/>
<EmployementStartDate/>
<EmployementEndDate/>
<UPN/>
<SAMAccountName/>
<Functions>
<Function>
<StartDate>2011-10-10T00:00:00.0000000+02:00</StartDate>
<EndDate>2015-05-31T00:00:00.0000000+02:00</EndDate>
<CostCenter>sdrg gerg jytfjdqert htrh</CostCenter>
<CostCenterCode/>
<Position>ihygkhgkjhgv A</Position>
</Function>
</Functions>
<Contracts>
<Contract>
<ID>ce369e13-f21f-4210-88e5-502bee3cdcc0</ID>
<StartDate>2015-10-10T00:00:00.0000000+02:00</StartDate>
<EndDate/>
<EmployementStartDate>2024-10-10T00:00:00.0000000+02:00</EmployementStartDate>
<EmployementEndDate/>
<Type>Onbepaalde tijd</Type>
<PhaseID>O</PhaseID>
<PB_CAO>true</PB_CAO>
<DissimelarCAO>VPB</DissimelarCAO>
</Contract>
<Contract>
<ID>ce369e13-f21f-4210-88e5-502bee3cdcc0</ID>
<StartDate>2011-10-10T00:00:00.0000000+02:00</StartDate>
<EndDate/>
<EmployementStartDate>2011-10-10T00:00:00.0000000+02:00</EmployementStartDate>
<EmployementEndDate/>
<Type>Onbepaalde tijd</Type>
<PhaseID>O</PhaseID>
<PB_CAO>true</PB_CAO>
<DissimelarCAO>VPB</DissimelarCAO>
</Contract>
</Contracts>
<Roosters>
<Rooster>
<StartDate>2011-10-10T00:00:00.0000000+02:00</StartDate>
<EndDate/>
<WeekAmountOfHours>30.5</WeekAmountOfHours>
</Rooster>
</Roosters>
<Wages>
<Wage>
<ID>c4df91b1-292d-464e-b17f-259968b9c39c</ID>
<StartDate>2016-12-05T00:00:00.0000000+01:00</StartDate>
<EndDate>2017-01-01T00:00:00.0000000+01:00</EndDate>
<HourlyWage>12.74</HourlyWage>
<RightTravelExpense>true</RightTravelExpense>
<RightTravelHours>true</RightTravelHours>
<RightAdditions>true</RightAdditions>
<MaxKm/>
<DissimelarZipCode/>
</Wage>
</Wages>
</Employee>
The result expect in output xml is to get the oldest "EmployementStartDate" so in this case is
<Employee xmlns="http://Securitas.ESB.HR.EmployeeUDMToRostar.MDSRequest_Schema">
<Number>157989</Number>
<SagaNumber>23776</SagaNumber>
<SurNameFull>Test</SurNameFull>
<FirstName>test</FirstName>
<GivenName>test</GivenName>
<Gender>Man</Gender>
<DateOfBirth>1992-04-07T00:00:00.0000000+02:00</DateOfBirth>
<EmailPrivate>test#hotmail.com</EmailPrivate>
<PhoneMobilePrivate>5145454558</PhoneMobilePrivate>
<PhonePrivate/>
<EmployementStartDate>2011-10-10T00:00:00.0000000+02:00</EmployementStartDate>
.
.
.
</Employee>

Walk/loop through an XSL key: how?

Is there a way to walk-through a key and output all the values it contains?
<xsl:key name="kElement" match="Element/Element[#idref]" use="#idref" />
I though of it this way:
<xsl:for-each select="key('kElement', '.')">
<li><xsl:value-of select="." /></li>
</xsl:for-each>
However, this does not work. I simply want to list all the values in a key for testing purposes.
The question is simply: how can this be done?
You can't. That's not what keys are for.
You can loop through every element in a key using a single call to key() if and only if the key of each element is the same.
If you need to loop over everything the key is defined over, you can use the expression in the match="..." attribute of your <key> element.
So if you had a file like this:
<root>
<element name="Bill"/>
<element name="Francis"/>
<element name="Louis"/>
<element name="Zoey"/>
</root>
And a key defined like this:
<xsl:key name="survivors" match="element" use="#name"/>
You can loop through what the key uses by using the contents of its match attribute:
<xsl:for-each select="element">
<!-- stuff -->
</xsl:for-each>
Alternatively, if each element had something in common:
<root>
<element name="Bill" class="survivor"/>
<element name="Francis" class="survivor"/>
<element name="Louis" class="survivor"/>
<element name="Zoey" class="survivor"/>
</root>
Then you could define your key like this:
<xsl:key name="survivors" match="element" use="#class"/>
And iterate over all elements like this:
<xsl:for-each select="key('survivors', 'survivor')">
<!-- stuff -->
</xsl:for-each>
Because each element shares the value "survivor" for the class attribute.
In your case, your key is
<xsl:key name="kElement" match="Element/Element[#idref]" use="#idref" />
So you can loop through everything it has like this:
<xsl:for-each select="Element/Element[#idref]">
<!-- stuff -->
</xsl:for-each>
You CAN create a key to use for looping - if you simply specify a constant in the use attribute of the key element:
<xsl:key name="survivors" match="element" use="'all'"/>
Then you can loop over all elements in the following way:
<xsl:for-each select="key('survivors','all')">
...
</xsl:for-each>
Or count them:
<xsl:value-of select="count(key('survivors','all'))"/>
Note that the constant can be any string or even a number - but 'all' reads well.
However, you cannot use this key to lookup information about the individual entries (because they all have the same key).
In other words there are two types of possible keys:
"lookup keys" = standard keys with varying indexes in the use attribute
"looping keys" = keys with a constant in the use attribute
I do not know how efficient this method is to execute, it does however make the maintenance of the XSL more efficient by avoiding repetition of the same (potentially very complex) XPath expression throughout the XSL code.
Rather than think of the XSL keys in programming language terms, think of them as record sets of SQL. That will give a better understanding. For a given key index created as
<xsl:key name="paths" match="path" use="keygenerator()">
it can be "iterated"/"walk-through" as below
<xsl:for-each select="//path[generate-id()=generate-id(key('paths',keygenerator())[1])]">
To understand this magic number [1], let s go through the below example :
Consider this XML snippet
<root>
<Person>
<name>Johny</name>
<date>Jan10</date>
<cost itemID="1">34</cost>
<cost itemID="1">35</cost>
<cost itemID="2">12</cost>
<cost itemID="3">09</cost>
</Person>
<Person>
<name>Johny</name>
<date>Jan09</date>
<cost itemID="1">21</cost>
<cost itemID="1">41</cost>
<cost itemID="2">11</cost>
<cost itemID="2">14</cost>
</Person>
</root>
transformed using this XSL.
<xsl:for-each select="*/Person">
<personrecords>
<xsl:value-of select="generate-id(.)" />--
<xsl:value-of select="name"/>--
<xsl:value-of select="date"/>--
</personrecords>
</xsl:for-each>
<xsl:for-each select="*/*/cost">
<costrecords>
<xsl:value-of select="generate-id(.)" />--
<xsl:value-of select="../name"/>--
<xsl:value-of select="../date"/>--
<xsl:value-of select="#itemID"/>--
<xsl:value-of select="text()"/>
</costrecords>
</xsl:for-each>
The above XSL transformation lists the unique id of the Person nodes and the cost nodes in the form of idpxxxxxxx as the result below shows.
1. <personrecords>idp2661952--Johny--Jan10-- </personrecords>
2. <personrecords>idp4012736--Johny--Jan09-- </personrecords>
3. <costrecords>idp2805696--Johny-- Jan10-- 1-- 34</costrecords>
4. <costrecords>idp4013568--Johny-- Jan10-- 1-- 35</costrecords>
5. <costrecords>idp2808192--Johny-- Jan10-- 2-- 12</costrecords>
6. <costrecords>idp2808640--Johny-- Jan10-- 3-- 09</costrecords>
7. <costrecords>idp2609728--Johny-- Jan09-- 1-- 21</costrecords>
8. <costrecords>idp4011648--Johny-- Jan09-- 1-- 41</costrecords>
9. <costrecords>idp2612224--Johny-- Jan09-- 2-- 11</costrecords>
10.<costrecords>idp2610432--Johny-- Jan09-- 2-- 14</costrecords>
Let us create a key on the cost records using a combination of name and itemID values.
<xsl:key name="keyByNameItem" match="cost" use="concat(../name, '+', #itemID)"/>
Manually looking at the XML, the number of unique keys for the above would be three : Johny+1, Johny+2 and Johny+3.
Now lets test out this key by using the snippet below.
<xsl:for-each select="*/*/cost">
<costkeygroup>
<xsl:value-of select="generate-id(.)" />--
(1)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[1] ) " />--
(2)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[2] ) " />--
(3)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[3] ) " />--
(4)<xsl:value-of select="generate-id(key('keyByNameItem',concat(../name, '+', #itemID) )[4] ) " />
</costkeygroup>
</xsl:for-each>
And here is the result:
1. <costkeygroup>idp2805696-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
2. <costkeygroup>idp4013568-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
3. <costkeygroup>idp2808192-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
4. <costkeygroup>idp2808640-- (1)idp2808640-- (2)-- (3)-- (4)</costkeygroup>
5. <costkeygroup>idp2609728-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
6. <costkeygroup>idp4011648-- (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648</costkeygroup>
7. <costkeygroup>idp2612224-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
8. <costkeygroup>idp2610432-- (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)</costkeygroup>
Our interest is in trying to understand the importance of [1],[2], [3],[4]. In our case, the keygenerator is concat(../name, '+', #itemID).
For a given key, [1] refers to the first occurence of a node that satisfies the keygenerator. Similarly [2] refers to the second occurence of a node that satisfies the keygenerator. Thus [2], [3],[4], etc. are all nodes that satisfy the same key, and thus can be considered duplicates for the given key. The number of duplicates depends on the input XML. Thus:
Key Johny+1 satisfies 4 nodes (1)idp2805696-- (2)idp4013568-- (3)idp2609728-- (4)idp4011648
Key Johny+2 satisfies 3 nodes (1)idp2808192-- (2)idp2612224-- (3)idp2610432-- (4)
Key Johny+3 satisfies 1 node (1)idp2808640-- (2)-- (3)-- (4)
Thus we see that ALL 8 cost nodes of the XML can be accessed through the key.
Here is a image that combines the transformation results to help better understand.
The red squares indicate the matching nodes for Johny+1. The green squares indicate the matching nodes for Johny+3. Match the idpxxxxxxx values in <costkeygroup> to the values in <costrecords>. The <costrecords> help map the idpxxxxxxx values to the source XML.
The takeaway is that,
an XSL key does not filter or eliminate nodes. All nodes including duplicates can be accessed through the key. Thus when we say "walk through" of the key, there is no concept of a resultant subset of nodes from the original set of nodes made available to the key for processing.
To "walk through" only unique nodes of the key in the above example, use
<xsl:for-each select="*/*/workTime[generate-id()=generate-id(key('keyByNameItem', concat(../name, '+', #itemID) )[1] ) ] ">
[1] signifies that the first record for a given key value is denoted as the unique record. [1] is almost always used because there will exist at least one node that satisfies a given key value. If we are sure that there will be a minimum of 2 records to satisfy each key value in the key, we can go ahead and use [2] to identify the second record in the record set as the unique record.
P.S The words nodes / records / elements are used interchangeably.
There is no way to walk-through the keys, although we can output all the values it contains. In XSLT2 it is quite easier than in XSLT1 (e.g., using fn:generate-id according to the previous answer).
Using fn:distinct-values
<xsl:variable name="e" select="."/>
<xsl:for-each select="distinct-values(Element/Element[#idref]/#idref)">
<li key="{.}"><xsl:value-of select="key('kElement', ., $e )" /></li>
</xsl:for-each>
Using xsl:for-each-group
<xsl:for-each-group select="Element/Element[#idref]" group-by="#idref">
<li key="{current-grouping-key()}"><xsl:value-of select="current-group()" /></li>
</xsl:for-each-group>