how to fetch the other tags by targeting a particular tag - xslt

Here I have pasted a sample of XML that was tag names like 21A,50F,21D,22B.
Normally if I need to fetch a particular tag, I can use logic below easily in XSLT:
<xsl:choose>
<xsl:when test="tag[name = '21A'] ">
<xsl:choose>
<xsl:when test="substring(tag[name = '21A']/value,1,1) = '/'">
<xsl:variable name="result" select="concat(translate(tag[name = '21A']/value,',',' '),'
')"/>
<xsl:value-of select="substring(substring-before(substring-after($result,'
'),'
'),1,11)"/>
</xsl:when>
<xsl:when test="substring(tag[name = '21A']/value,1,1) != '/'">
<xsl:value-of select="substring(tag[name = '21A']/value,1,11)"/>
</xsl:when>
</xsl:choose>
</xsl:when>
</xsl:choose>
but here I have a requirement as Sequence A and Sequence B.
the tags which populated above 50F come under sequence A
the tags which populated below 50F come under sequence B
since we need to fetch the tags based on using 50F tag. Can any one please give a suggestion?
<local>
<message>
<block4>
<tag>
<name>21A</name>
<value>ALW1031</value>
</tag>
<tag>
<name>50F</name>
<value>TESTING CITI BANK EFT9</value>
</tag>
<tag>
<name>21D</name>
<value>OUR</value>
</tag>
<tag>
<name>22B</name>
<value>ipubby</value>
</tag>
</block4>
</message>
</local>
output required:
ALW1031,OUR
Previously if suppose they have populate 21A two times means I have used the position as [1] and [2] as while calling tag values. Now they will populate 21 tag repeatedly but tags may be A or D so I need to target 50f tag blindly. Whatever tag they will provide, either A or D before 50F I need to fetch similarly whatever they populate tags after 50F we able to fetch so avoiding positions.
Summary:
#Treemonkey: hope you had a glance of my sample XML. It has some tag like 21A,50F and so on. Assume if I have two fields field1,field2 earlier they have populated tags as same repeated tags as 21A at that time I have fetched as 21A having beside by marking position [1] for field 1 (tag[name = '21A'][1])
Similarly 21A having beside by marking position [2] for field 2, now they will populate 21 but tags were different as A or D. As I have said field1 should concentrate sequence A and field2 should concentrate as sequence B so now we should not bother about positions for fetch we have a demarcation like tag 50F whatever fields will populate before 50F has to treated as sequence A and after 50F has to be treated as sequence B.
So finally we need to write as XSLT by targeting 50F. If I want to display 21A field in (sample XML) which before 50F so we need write a logic in XSLT as select tag 21A before 50F tag for to produce data in field 1 and for field 2 we need to fetch as 21D after 50F so we need to write a logic as select 21D after 50F.

Your requirements aren't exactly clear, but the following expressions should help.
Select all siblings before the tag whose child name has a particular value:
/*/*/*/tag[name='50F']/preceding-sibling::*
Select following siblings:
/*/*/*/tag[name='50F']/following-sibling::*
Select just the immediately preceding element:
/*/*/*/tag[name='50F']/preceding-sibling::*[1]
Select preceding siblings having a child name with a particular value:
/*/*/*/tag[name='50F']/preceding-sibling::*[name='21A']
Select following siblings having a child name with a particular value:
/*/*/*/tag[name='50F']/following-sibling::*[name='21D']

Related

XSLT 1.0 - change the order of lines

I want to change the order of line cac:InvoiceLine depending on this node:
cac:AdditionalItemProperty/cbc:Value
All InvoiceLines that have Item type=RC must be gruop at the end of lines, and all that have CU must be on the top.
If the mentioned values are the only ones you are concerned about, then it seems like you could just sort alphabetically by that value; see xsl:sort. You could just put this inside the xsl:for-each or xsl:apply-templates where you process your invoice lines:
<xsl:sort select="cac:AdditionalItemProperty/cbc:Value" />
On the other hand, if you only want to output only the line items with the mentioned values, you could select them separately. For example, assuming you have a template which matches your invoice lines, you'd first apply it to the 'CU' ones and then to the 'RC' ones:
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='CU']" />
<xsl:apply-templates select="cac:InvoiceLine[cac:AdditionalItemProperty/cbc:Value='RC']" />

Selecting multiple elements including current

I have data structured like this:
<url title="Stack Overflow">http://stackoverflow.com/</url>
<url>http://example.com/</url>
The title attribute is optional. I have an XSLT stylesheet including this:
<xsl:template match="url">
<xsl:value-of select="(#title | .)[1]"/>
</xsl:template>
I want to display the title if there is one and otherwise display the url. However for some reason the title is never displayed. (In every case the url is displayed.)
What am I doing wrong?
Thanks!
Try either:
<xsl:value-of select="(#title | text())[1]"/>
or:
<xsl:value-of select="(#title | .)[last()]"/>
Explanation:
The . abbreviation in your expression represents the url element itself, which precedes its attributes in document order.

Manage flags in xslt?

All,
i am searching in a list of fields those who has the type clob and i am writing it separed by a comma like this [field1, field2, ... fieldn]
my problem is how to identify the first matched field to write it without comma ( i can't use position() because the first field matched can be the first of the list or the last of the list)
I want to make this algorithm in xslt,
variable is_first = TRUE;
if(is_first) {
do smthng;
isfirst = False;
}
Actually it is not possible to make something like this in xslt since variable are immutable. There probably could be workarounds but you have to specify your need in more details.
edit:
If your input is string with values separated by commas...
<xsl:variable name="inputString" select="'field1,field2,field3a,field4,field3b'" />
... you could use tokenize() functions...
<xsl:variable name="tokenized" select="tokenize($inputString, ',')" />
... and then select items corresponding to your condition
<!-- Select item corresponding to condition (e.g. it contains 3). Take first one if there are several such items -->
<xsl:value-of select="$tokenized[contains(., '3')][1]" />
Edit2:
You can use separator attribute of xsl:value-of (xslt 2.0) for output of delimited values.
Assuming following variable
<xsl:variable name="list">
<item>first</item>
<item>second</item>
<item>third</item>
</xsl:variable>
this <xsl:value-of select="$list/item" separator="," /> makes desired output first,second,third
You need to write this using functional code rather than procedural code. It's not possible to do the conversion without seeing the context (it's much easier to work from the problem rather than from the solution in a lower-level language).
But the most common equivalent in XSLT would take the form
<xsl:for-each select=".....">
<xsl:if test="position() = 1"><!-- first time code --></xsl:if>
....
</xsl:for-each>

Using <xsl:for-each> for incremental element names

I have an XML that is converted from a java map. So all the map keys are converted into node names. The XML structure is as below
<map>
<firstName>AAA</firstName>
<firstName1>BBB</firstName1>
<firstName2>CCC</firstName2>
<firstName3>DDD</firstName3>
</map>
I am trying to write a for-each loop to extract data from this XML to create an output XML. I have tried most of the options available such as name(), local-name(), contains(), etc but couldn't come up with something that worked. What are the options available since the incremental node name can go upto count 100 or more. Any inputs in coding the loop would be of great help. I am using XSLT 1.0.
There are many ways to select the children of the top element (map):
/*/*
This selects all elements that are children of the top element of the XML document.
/*/*[starts-with(name(), 'firstName')]
This selects all top element's children-elements, whose name starts with the string 'firstName'.
/*/*[starts-with(name(), 'firstName')
and floor(substring-after(name(), 'firstName')) = substring-after(name(), 'firstName')) ]
This selects all top element's children-elements, whose name starts with the string 'firstName' and the remaining substring after this is an integer.
/*/*[starts-with(name(), 'firstName')
and translate(name(), '0123456789', '') = 'firstName')) ]
This selects all top element's children-elements, whose name starts with the string 'firstName' and the remaining substring after this contains only digits.
Finally, in XPath 2.0 (XSLT 2.0) one can use regular expressions:
/*/*[matches(name(), '^firstName\d+$')]
This will select all the first level elements and their information, which you can then use as you wish:
<xsl:for-each select="/*/*">
<xsl:value-of select="local-name()"/>
<xsl:value-of select="."/>
</xsl:for-each>

convert <xsl:variable> string value , to the value which I can select with <xsl:value-of select>

I have a variable from which I need to dynamically generate nodes
<xsl:template match="banner_discount_1 | banner_discount_2 | banner_discount_3">
<xsl:variable name="link">banner_discount_<xsl:value-of select="substring-after(name(.) ,'banner_discount_')" />_link</xsl:variable>
<xsl:value-of select="$link" />
</xsl:template>
<xsl:value-of> selects the string, but I want to be able to select the node which name matches the name of a variable.
In my case the node looks something like this:
<banner_discount_1_link />
<banner_discount_2_link />
...
Here is the xml I'm using
<banner_discount_1> 12 </banner_discount_1>
<banner_discount_2> 21 </banner_discount_2>
<banner_discount_3> 32 </banner_discount_3>
<banner_discount_1_link> link1 </banner_discount_1_link>
<banner_discount_2_link> link2 </banner_discount_2_link>
<banner_discount_3_link> link3 </banner_discount_3_link>
#MartinHonnen is on the right track, but you need to set the selection context as well.
Since you're in a template that's selecting the banner_discount_ nodes, that is your context. From your XML sample, it looks like the nodes you want to select are siblings, so this should work:
<xsl:value-of select="../*[local-name() = $link]"/>
It is preferable to target the nodes directly, but if they could be anywhere in the document, then you may resort to
<xsl:value-of select="//*[local-name() = $link]"/>
This is a last resort because it is potentially O(n) with respect to the number of nodes in the document.
Use <xsl:value-of select="*[local-name() = $link]"/>. If that does not help then consider to show a sample of the XML.