choosing specific column value from the input xml - xslt

i have the below xml as input for which i have to do the xsl transformation
<emml>
<tradeEventHeader>
<tradeIdentifier>
<tradeId>104823343913</tradeId>
<systemReference>RDS</systemReference>
<systemDomainName>Internal</systemDomainName>
</tradeIdentifier>
<tradeStateIdentifier>
<tradeStateId>Validated</tradeStateId>
<systemReference>RDS</systemReference>
<tradeStateIdClassificationScheme>Vn State</tradeStateIdClassificationScheme>
</tradeStateIdentifier>
<tradeStateIdentifier>
<tradeStateId>Pending</tradeStateId>
<systemReference>Swapswire</systemReference>
<tradeStateIdClassificationScheme>Mang State</tradeStateIdClassificationScheme>
</tradeStateIdentifier>
<tradeStateIdentifier>
<tradeStateId>accpt_novated_sw</tradeStateId>
<systemReference>RDS</systemReference>
<tradeStateIdClassificationScheme>Clearing State</tradeStateIdClassificationScheme>
</tradeStateIdentifier>
</tradeEventHeader>
<emmlExtension systemId="RDS YTO">
<emmlMediumString idref="legId1" name="Roll Date Option">Short Initial</emmlMediumString>
</emmlExtension>
</emml>
as shown above in the input xml basically my objective is to identify the value of tradeStateIdClassificationScheme parameter and if the value of this parameter is equal to 'Clearing state' then with correspond to that i have to check the value of another column tradeStateId and if the value of the column tradeStateId starts with accpt_novated_sw then in that case we need to return true string and for rest other i need to return false string ..
i have come up with the below template in xslt 1.0 , please advise is it correct approach..
calling template :-
<isClearedNovated>
<xsl:call-template name="cleared_novated">
<xsl:with-param name="tradeStateId" select="emml/*/*/tradeStateIdentifier" />
</xsl:call-template>
</isClearedNovated>
called template :-
<xsl:template name="cleared_novated">
<xsl:param name="tradeStateId" />
<xsl:for-each select="$tradeStateId/tradeStateIdClassificationScheme">
<xsl:choose>
<xsl:when test="$tradeStateId[starts-with(tradeStateIdClassificationScheme,'accpt')] and systemReference='RDS'">
<xsl:value-of select="'true'"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="'false'"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:template>

I don't really understand, what exactly your needs are, but your XSLT probably does not what you want - I suspect it does nothing...
So maybe we can start with the suggestion below and you can tell, what has to be refined:
<?xml version="1.0" encoding="utf-8"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:apply-templates select="//tradeStateIdClassificationScheme"/>
</xsl:template>
<xsl:template match="tradeStateIdClassificationScheme[
. = 'Clearing State' and
../tradeStateId = 'accpt_novated_sw' and
../systemReference = 'RDS'
]">
<xsl:value-of select="concat(.,': true
')"/>
</xsl:template>
<xsl:template match="tradeStateIdClassificationScheme">
<xsl:value-of select="concat(.,': false
')"/>
</xsl:template>
<xsl:template match="#*|*"/>
</xsl:transform>
You find two templates dealing with tradeStateIdClassificationScheme, one matches your conditions, and one for all others.
Note that you didn't write about the contents of systemReference, while your trial template addresses this element. Therefore, I added this condition as well.
The output in this version is:
Vn State: false
Mang State: false
Clearing State: true

Related

check for successively numbered attributes

I have a situation where I need to check for attribute values that may be successively numbered and input a dash between the start and end values.
<root>
<ref id="value00008 value00009 value00010 value00011 value00020"/>
</root>
The ideal output would be...
8-11, 20
I can tokenize the attribute into separate values, but I'm unsure how to check if the number at the end of "valueXXXXX" is successive to the previous value.
I'm using XSLT 2.0
You can use xsl:for-each-group with #group-adjacent testing for the number() value subtracting the position().
This trick was apparently invented by David Carlisle, according to Michael Kay.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vals"
select="tokenize(root/ref/#id, '\s?value0*')[normalize-space()]"/>
<xsl:variable name="condensed-values" as="item()*">
<xsl:for-each-group select="$vals"
group-adjacent="number(.) - position()">
<xsl:choose>
<xsl:when test="count(current-group()) > 1">
<!--a sequence of successive numbers,
grab the first and last one and join with '-' -->
<xsl:sequence select="
string-join(current-group()[position()=1
or position()=last()]
,'-')"/>
</xsl:when>
<xsl:otherwise>
<!--single value group-->
<xsl:sequence select="current-group()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:variable>
<xsl:value-of select="string-join($condensed-values, ',')"/>
</xsl:template>
</xsl:stylesheet>

How to declare a sequence in XSLT?

I need to declare a fixed sequence of numbers. How do I do this?
For example, is it (I'm guessing here):
<xsl:element name="xsl:param">
<xsl:attribute name="name">MySequence</xsl:attribute>
<xsl:sequence>(1,2,3,4)</xsl:sequence>
</xsl:element>
or
<xsl:element name="xsl:param">
<xsl:attribute name="name">MySequence</xsl:attribute>
<xsl:sequence>1,2,3,4</xsl:sequence>
</xsl:element>
or what?
Thanks
If you're using XSLT 2.0, you can just create the sequence directly in the select like:
<xsl:param name="MySequence" select="('1','2','3','4')"/>
XSLT based verification...
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:param name="seq" select="('23453','74365','98','653')"/>
<xsl:template match="/">
<xsl:for-each select="$seq">
<xsl:value-of select="concat('Item ',position(),': ',.,'
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
applied to any XML input produces:
Item 1: 23453
Item 2: 74365
Item 3: 98
Item 4: 653
To build a sequence in the XSLT 2.0 sense you use a select e.g.
<xsl:sequence select="1 to 4" />
But if you're adding the value to an element you may prefer value-of
<xsl:value-of select="1 to 4" separator="," />
Given the snippet in the question, this would generate output XML of
<xsl:param name="MySequence">1,2,3,4</xsl:param>
Which makes the value of the generated param a comma separated string. If you actually want the param value to be a sequence in the generated XSLT then you need to generate a select attribute instead of using element content
<xsl:element name="xsl:param">
<xsl:attribute name="name" select="'MySequence'"/>
<xsl:attribute name="select">
<xsl:text>(</xsl:text>
<xsl:value-of select="1 to 4" separator=","/>
<xsl:text>)</xsl:text>
</xsl:attribute>
</xsl:element>
Giving output of
<xsl:param name="MySequence" select="(1,2,3,4)" />

Testing whether a node with particular content exists using xslt

I am trying to merge the elements from two separate web.xml files using XSLT. For example, if web-1.xml and web-2.xml are being merged, and I'm processing web-1.xml, I want all elements in web-2.xml to be added into the result, except any that already exist in web-1.xml.
In the XSLT sheet, I have loaded the document whose servlet's are to be merged into the other document using:
<xsl:variable name="jandy" select="document('web-2.xml')"/>
I then have the following rule:
<xsl:template match="webapp:web-app">
<xsl:copy>
<!-- Copy all of the existing content from the document being processed -->
<xsl:apply-templates/>
<!-- Merge any <servlet> elements that don't already exist into the result -->
<xsl:for-each select="$jandy/webapp:web-app/webapp:servlet">
<xsl:variable name="servlet-name"><xsl:value-of select="webapp:servlet-name"/></xsl:variable>
<xsl:if test="not(/webapp:web-app/webapp:servlet/webapp:servlet-name[text() = $servlet-name])">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
</xsl:copy>
</xsl:template>
The problem I'm having is getting the test in the if correct. With the above code, the test always evaluates to false, whether a servlet-name element with the given node exists or not. I have tried all kinds of different tests but with no luck.
The relevant files are available at http://www.cs.hope.edu/~mcfall/stackoverflow/web-1.xml, and http://www.cs.hope.edu/~mcfall/stackoverflow/transform.xslt (the second web-2.xml is there as well, but StackOverflow won't let me post three links).
provide an anchor for the first document, just before the for-each loop:
<xsl:variable name="var" select="."/>
then, use it in your if:
<xsl:if test="not($var/webapp:servlet/webapp:servlet-name[text() = $servlet-name])">
Your template matches XPATH webapp:webapp from web-1.xml and
you are refencing absolute XPATH if your xsl:if condition: /webapp:web-app/webapp:servlet/webapp:servlet-name[text() = $servlet-name]. Try to do it using relative XPATH:
<xsl:if test="not(webapp:servlet/webapp:servlet-name[text() = $servlet-name])">
<xsl:copy-of select="."/>
</xsl:if>
I haven't checked it, so you have to give it a try.
Also, it would be easier if you could provide web-1.xml and web-2.xml files.
EDIT
The following XSLT merges two files - the only problem appears when there are sections of the same type (like listener) in two places of the input XML.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:webapp="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xpath-default-namespace="http://java.sun.com/xml/ns/javaee">
<xsl:output indent="yes"/>
<xsl:variable name="jandy" select="document('web-2.xml')"/>
<xsl:template match="/">
<xsl:element name="web-app">
<xsl:for-each select="webapp:web-app/*[(name() != preceding-sibling::node()[1]/name()) or (position() = 1)]">
<xsl:variable name="nodeName" select="./name()"/>
<xsl:variable name="web1" as="node()*">
<xsl:sequence select="/webapp:web-app/*[name()=$nodeName]"/>
</xsl:variable>
<xsl:variable name="web2" as="node()*">
<xsl:sequence select="$jandy/webapp:web-app/*[name() = $nodeName]"/>
</xsl:variable>
<xsl:copy-of select="$web1" copy-namespaces="no"/>
<xsl:for-each select="$web2">
<xsl:variable name="text" select="./*[1]/text()"/>
<xsl:if test="count($web1[*[1]/text() = $text]) = 0">
<xsl:copy-of select="." copy-namespaces="no"/>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

XSLT: nested for-each and dynamic variable

This is my XML and XSLT code
<root>
<act>
<acts id>123</acts>
</act>
<comp>
<comps id>233</comps>
</comp>
</root>
<xsl:for-each select="act/acts">
<xsl:variable name="contactid" select="#id"/>
<xsl:for-each select="root/comp/comps">
<xsl:variable name="var" select="boolean(contactid=#id)"/>
</xsl:for-each>
<xsl:choose>
<xsl:when test="$var='true'">
. . . do this . . .
</xsl:when>
<xsl:otherwise>
. . . do that . . .
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
I want to dynamically assign true or false to var and use it inside <xsl:choose> for boolean test. I hope this helps to find a better solution to get rid of for-each also
First thing to note is that variables in XSLT are immutable, and cannot be changed once initialised. The main problem with your XSLT is that you define your variable within an xsl:for-each block and so it only exists within the scope of that block. It is not a global variable. A new variable gets defined each time that can only be used within the xsl:for-each
From looking at your XSLT it looks like you want to iterate over the acts element and perform a certain action depending on whether an comps element exists with the same value. An alternative approach would be to define a key to look up the comps elements, like so
<xsl:key name="comps" match="comps" use="#id" />
Then you can simply check whether a comps element exists like so (assuming you are positioned on an acts element.
<xsl:choose>
<xsl:when test="key('comps', #id)">Yes</xsl:when>
<xsl:otherwise>No</xsl:otherwise>
</xsl:choose>
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="comps" match="comps" use="#id" />
<xsl:template match="/root">
<xsl:apply-templates select="act/acts" />
</xsl:template>
<xsl:template match="acts">
<xsl:choose>
<xsl:when test="key('comps', #id)"><res>Yes</res></xsl:when>
<xsl:otherwise><res>No</res></xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When applied to the following (well-formed) XML
<root>
<act>
<acts id="123"/>
</act>
<comp>
<comps id="233"/>
</comp>
</root>
The following is output
No
However, it can often be preferably in XSLT to avoid the use of conditional statements like xsl:choose and xsl:if. Instead, you can structure the XSLT to make use of template matching. Here is the alternate approach
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="comps" match="comps" use="#id" />
<xsl:template match="/root">
<xsl:apply-templates select="act/acts" />
</xsl:template>
<xsl:template match="acts[key('comps', #id)]">
<res>Yes</res>
</xsl:template>
<xsl:template match="acts">
<res>No</res>
</xsl:template>
</xsl:stylesheet>
When applied to the same XML, the same result is output. Do note the more specific template for the acts node will take priority when matching the case where a comps exist.
There are some errors in your xml file, but assuming what you mean is:
<root>
<act><acts id="123"></acts></act>
<comp><comps id="233"></comps></comp>
</root>
Here is a full solution:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<doc>
<xsl:apply-templates select="root/comp/comps"/>
</doc>
</xsl:template>
<xsl:template match="root/comp/comps">
<xsl:variable name="compsid" select="#id"></xsl:variable>
<xsl:choose>
<xsl:when test="count(/root/act/acts[#id=$compsid])>0">Do This</xsl:when>
<xsl:otherwise>Do That</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>

xsl get array of elements

Hi
I need get array of elements (before "-" if exist) by xsl.
xml is
<Cars>
<Car Trunck="511"/>
<Car Trunck="483-20"/>
<Car Trunck="745"/>
</Cars>
xsl is
<xsl:variable name="testarr">
<xsl:for-each select="//Cars//Car/#Trunck">
<xsl:value-of select="number(substring(.,1,3))" />
</xsl:for-each>
</xsl:variable>
(i suppose that all numbers is three-digit number, if someone knows a solution for all conditions will be glad to hear the proposal)
if i do this
i get all numbers in one line: 511483745
and i need get them in array
because i also need get the max value
thanks
Hi I need get array of elements
(before "-" if exist) [...] i need get
them in array because i also need get
the max value
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:for-each select="/Cars/Car/#Trunck">
<xsl:sort select="concat(substring-before(.,'-'),
substring(., 1 div not(contains(.,'-'))))"
data-type="number" order="descending"/>
<xsl:if test="position()=1">
<xsl:value-of
select="concat(substring-before(.,'-'),
substring(.,1 div not(contains(.,'-'))))"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
745
XPath 2.0 one line:
max(/Cars/Car/#Trunck/number(replace(.,'-.*','')))
You could use the substring-before and substring-after functions: See the excellent ZVON tutorial
http://zvon.org/xxl/XSLTreference/Output/function_substring-after.html
In your example you are only extracting the values (which are strings) which get concatenated. Perhaps you need to wrap the result in your own element
<xsl:for-each select="//Cars//Car/#Trunck">
<truck>
<xsl:value-of select="number(substring(.,1,3))" />
</truck>
</xsl:for-each>
While you have two good answers (especially that by #Alejandro), here's one from me that I think is even better:
This transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:param name="pTopNums" select="2"/>
<xsl:template match="/*">
<xsl:apply-templates select="*">
<xsl:sort data-type="number" order="descending"
select="substring-before(concat(#Trunck,'-'),'-')"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="Car">
<xsl:if test="not(position() > $pTopNums)">
<xsl:value-of select=
"substring-before(concat(#Trunck,'-'),'-')"/>
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document (the originally provided one, slightly changed to be more challenging):
<Cars>
<Car Trunck="483-20"/>
<Car Trunck="311"/>
<Car Trunck="745"/>
</Cars>
produces the wanted, correct result (the top two numbers that are derived from #Trunck as specified in the question):
745
483