why this Apply template was not working properly - xslt

if suppose my database having three row records by fetching that data i am converting into xml data as shown below :
Here is my XML:
<NewDataSet>
<Table>
<RECORD_TYPE_CODE>CTD</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O103N</MSG_TYPE_CODE>
<CTD_SEQ_NUM>000086</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>2</CTD_CTD_PKG_ID>
</Table>
<Table>
<RECORD_TYPE_CODE>CTO</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O203N</MSG_TYPE_CODE>
<CTD_SEQ_NUM>000087</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>2</CTD_CTD_PKG_ID>
</Table>
<Table>
<RECORD_TYPE_CODE>CTH</RECORD_TYPE_CODE>
<MSG_TYPE_CODE>O303N</MSG_TYPE_CODE>
<CTD_SEQ_NUM>000088</CTD_SEQ_NUM>
<CTD_CTD_PKG_ID>2</CTD_CTD_PKG_ID>
</Table>
</NewDataSet>
through c sharp code behind using for loop i m sending package id (CTD_CTD_PKG_ID)and Sequencenum(CTD_SEQ_NUM) through xslt argument parameters it was passing properly in a sequential order like 000086,000087,000088 but first time for in aloop i will pass 000086 seqnum it was fetching proper manner next when i m passing 000087 it was generating data in table of 000086 only why it was happening passing parameter value is goin good in a sequential order
here is my XSLT:
<xsl:param name="PackageId" />
<xsl:param name="SequenceNum" />
<xsl:template match="/">
<xsl:apply-templates mode="SequenceB" select="(NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId] | NewDataSet/Table[CTD_SEQ_NUM =$SequenceNum])[1]"/>
</xsl:template>

I guess (if my guess is incorrect, you need to explain better the problem) that the value of your $PackageId parameter is always defined as 2.
The XPath expression you are using:
(NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId]
|
NewDataSet/Table[CTD_SEQ_NUM =$SequenceNum]
)
[1]
selects the first node from the union of the nodes selected by:
NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId]
and:
NewDataSet/Table[CTD_SEQ_NUM =$SequenceNum]
Do note, that if the value of the $PackageId parameter is 2, then the first node of theis union is always /*/Table[1].
The solution is to replace the XPath expression you are using with:
NewDataSet/Table[CTD_CTD_PKG_ID =$PackageId][CTD_SEQ_NUM =$SequenceNum]

Related

Comparing attribute values in XPath

I want to get the value of a specific node, specified by its id attribute. But the behaviour of my XSL parser, Saxon, is not how I expected it to work.
This is my XSL code:
<xsl:template match="synonyme">
<xsl:element name="corr">
<xsl:value-of select="#connecteur" />
<xsl:value-of select="/liste/connecteur[#id=#connecteur]/forme[1]" />
</xsl:element>
</xsl:template>
I just matched a tag named synonyme which has a connecteur attribute. My code outputs the value of this attribute.
I also want to output the value of another node which has an id attribute matching the connecteur attribute of my currently matched synonyme tag. But no results are ever found for this query, the second value-of always has empty output.
If I write, e.g. [#id='c160'], where c160 is the exact same thing that is output by the first value-of, it works! But not when comparing to the #attribute of my matched node. How can I fix this?
The XML is basically
<liste><connecteur id="c160"><forme>foo</forme></connecteur>
<connecteur id="c161"><synonyme connecteur="c160" /></connecteur>
</liste>
and the expected output in place of the synonyme is <corr>c160 foo</corr>.
The predicate you use:
[#id=#connecteur]
is looking for an element with two attributes - id and connecteur- with equal values. To look for an element with an id attribute whose value matches the value of the current element's connecteur value, you need to use:
[#id=current()/#connecteur]
See: https://www.w3.org/TR/xslt/#function-current
A better solution would be to define a key as:
<xsl:key name="ref" match="connecteur" use="#id" />
then use:
<xsl:value-of select="key('ref', #connecteur)/forme" />
to resolve the cross-reference.
See: https://www.w3.org/TR/xslt/#key

Change text of elements identified by dynamic XPath

I have an XML with 2 XML fragments, 1st one is a fragment where the new values must be applied (which can have pretty complex elements) like
... some static parents
<a:element1>
<a:subelement tag="someString">
<a:s1>a</a:s1>
</a:subelement>
</a:element1>
<a:element2>b</a:element2>
<a:element3>c</a:element3>
... lots of other elements like the above ones
and 2nd fragment that has XPaths generated from the first XML and a new value, like
<field>
<xpath>/Parent/element1/subelement[#tag="someString"]/s1</xpath>
<newValue>1</newValue>
</field>
<field>
<xpath>/Parent/element2</xpath>
<newValue>2</newValue>
</field>
We might not have new values to apply for all the elements in the first fragment.
I'm struggling to make an XSLT transformation that should apply the new values to the places indicated by the XPaths.
The output should be:
... some static parents
<a:element1>
<a:subelement tag="someString">
<a:s1>1</a:s1>
</a:subelement>
</a:element1>
<a:element2>2</a:element2>
... lots of other elements like the above ones
I have access to xalan:evaluate to evaluate the dynamic xpath. I'm trying different solutions, I will write them here when they will start to make sense.
Any ideas of approaches are well received. Thanks
Oki, I found out how, and I will write the answer here maybe someone sometime will need this:
<xsl:template match="/">
<!-- static parents -->
<a:Root>
<xsl:apply-templates select="/a:Root/a:Parent" />
</a:Root>
</xsl:template>
<xsl:template match="#*|*|text()">
<xsl:variable name="x" select="generate-id(../.)" />
<xsl:variable name="y" select="//field[generate-id(xalan:evaluate(xpath)) = $x]" />
<xsl:choose>
<xsl:when test="$y">
<xsl:value-of select="$y/newValue" />
</xsl:when>
<xsl:otherwise>
<xsl:copy>
<xsl:apply-templates select="#*|*|text()" />
</xsl:copy>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
And to explain the transformation:
I'm writing down part that is static and then call apply-templates on the fragment I'm interested in, that has a liquid structure.
Then I'm using a slightly modified identity transformation that copies everything from source to target (starting from the /a:Root/a:Parent fragment), except when we position ourselves on the text I'm interested in changing.
The text() I'm interested in will have as parent (../.) the element referred by an xpath string found in the second fragment. Variable x means, in the context of the when, this element.
Variable y finds a field element that has as child an xpath element that if evaluated using xalan will refer to the same element that the x variable relates to.
Now I used generate-id() in order to compare the physical elements, otherwise it would have compared by the toString of the element (which is wrong). If variable y doesn't exist, it means that I have no xpath element for this element that could have changed, and I'm leaving it alone. If the y variable exists, I can get from it the newValue and I'm currently positioned on the element which text I want to update.

XSL: Calling specific Element from Multiple Instances of one Schema

I created a component named "BusLocationLinks" it stores the name of the business as well as the coordinates for a map I created.
I have nearly 50 business locations with that same schema (BusLocationsLinks) and only want to list the element "Business Name" of all components components of that name. I've tried everything but can not make them all display. ANY ADVICE?
Here's my current code:
<xsl:template name="BusLocationLinks">
<xsl:for-each select="BusLocationLinks/BusinessName">
<li class="active">
<xsl:value-of select="BusinessName" />
</li>
</xsl:for-each>
</xsl:template>
my xml code looks similar to such:
<BusLocationLinks>
<BusinessName>Star Property</BusinessName>
</BusLocationLinks>
Without seeing your XML it is difficult to diagnose the problem. However, it is likely that you have the following structure:
<BusLocationLinks>
<BusinessName>name1</BusinessName>
<BusinessName>name2</BusinessName>
<BusinessName>name3</BusinessName>
</BusLocationLinks>
If that is the case, then you should adjust your XSLT like this:
<xsl:template name="BusLocationLinks">
<xsl:for-each select="BusinessName">
<li class="active">
<xsl:value-of select="." />
</li>
</xsl:for-each>
</xsl:template>
The body of an xsl:for-each instruction resets the context node to be one of the nodes in the selected node set (a different one each time the body of the for-each is evaluated).
In your example, that means that within the body of the for-each, the current node is one of the BusLocationLinks/BusinessName elements you selected. Your loop creates a list-item element for each of them (check your output, I expect you'll see them there) containing the value of the BusinessName child of the context node. The context node matches the expression BusLocationLinks/BusinessName, so you are looking for the values of nodes which match BusLocationLinks / BusinessName / BusinessName. If you don't have any nodes that match the expression BusLocationLinks / BusinessName / BusinessName, you'll be getting empty li elements.
Try <xsl:value-of select="."/>.

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.

How to check if ancestor is last element of that type in his ancestor

I have a following XML:
<TABLE>
<ROW>
<ENTRY/>
<ENTRY/>
</ROW>
<ROW>
<ENTRY>xxx</ENTRY>
<ENTRY>yyy</ENTRY>
</ROW>
<Z>
<ROW>
<ENTRY/>
<ENTRY/>
</ROW>
</Z>
<ROW>
<ENTRY/>
<ENTRY/>
</ROW>
</TABLE>
The table structure can change, so I could have last row wrapped in Z element, or there could be no Z element at all.
My goal is to remove the bottom border in entries of the last row in table.
(TABLE maps to HTML table, ROW to HTML tr, ENTRY to HTML td)
so I try to use xslt template:
<xsl:template match="ENTRY">
<td>
<xsl:if test="(ancestor::ROW[1] = ancestor::TABLE[1]/descendant::ROW[last()])">
<!-- remove the bottom border of td -->
</xsl:if>
</td>
</xsl:template>
But it removes the border from almost all the cells in table (the border is not removed from cells that have text content). So i guess the mechanism checks if the nodes have the same value (name, text content, children and so on) - not references.
I've tried to use some external function in java, but I always get a new reference of node (even for the same nodes) so I can't compare it.
So my question is - how to compare the references of nodes...
or do the job in any other way.
Thanks in advance :).
My goal is to remove the bottom border in entries of the last row in
table. (TABLE maps to HTML table, ROW to HTML tr, ENTRY to HTML td)
It seems that you want:
<xsl:if test=
"generate-id(ancestor::ROW[1])
=
generate-id(ancestor::TABLE[1]/descendant::ROW[last()])">
<!-- Processing here -->
</xsl:if>
I recommend that you add a new template and use pattern matching (instead of explicit conditional) to capture this case:
<xsl:template match=
"ENTRY[generate-id(ancestor::ROW[1])
=
generate-id(ancestor::TABLE[1]/descendant::ROW[last()])
]">
<!-- Processing here -->
</xsl:template>
Your more general question:
So my question is - how to compare the references of nodes
In XPath 2.0 (XSLT 2.0) use the is operator:
$n1 is $n2
tests the two nodes $n1 and $n2 for node identity (not value equality).
In XPath 1.0 (XSLT 1.0):
count($n1|$n2) = 1
Another way in XSLT 1.0 is to use the standard XSLT 1.0 (not available in XPath 1.0) function generate-id(), as done in the above solution:
generate-id($n1) = generate-id($n2)
Your test (ancestor::ROW[1] = ancestor::TABELA[1]/descendant::ROW[last()]) matches the first two and the last two ENTRY elements in your sample. Presumably because when rendered as strings the first ROW matches the last ROW. See http://www.w3.org/TR/xpath/#booleans
I was able to select the last two entries using:
//ENTRY[count(ancestor::ROW[1]/preceding::ROW) = count(ancestor::TABLE[1]/descendant::ROW) - 1]
So I guess that the following would work as your test:
(count(ancestor::ROW[1]/preceding::ROW) = count(ancestor::TABLE[1]/descendant::ROW) - 1)
Update Courtesy of feedback from Dimitre Novatchev and jasso
This solution is not ideal as:
These counts are inefficient, counting more things than necessary.
preceding:: axis may match more than just the descendants of the TABLE element when part of a larger document.