Difference between ancestor and ancestor-or-self - xslt

I know about ancestor in xpath but what is this ancestor-or-self.
when we have to use ancestor-or-self.Please give me any examples.

The axis name is quite self-explanatory I think. ancestor axis selects only ancestor(s) of current context element, while ancestor-or-self selects both ancestor(s) and the current element itself. Consider the following XML for example :
<root>
<item key="a">
<item key="b" target="true">
<context key="c" target="true"/>
</item>
</item>
</root>
The following xpath which uses ancestor axis, will find the item b because it has target attribute equals true and b is ancestor of context element. But the XPath won't select context element itself, despite it has target equals true :
//context/ancestor::*[#target='true']
output of the above XPath in xpath tester :
Element='<item key="b" target="true">
<context key="c" target="true" />
</item>'
contrasts with ancestor-or-self axis which will return the same, plus context element :
//context/ancestor-or-self::*[#target='true']
output of the 2nd XPath :
Element='<item key="b" target="true">
<context key="c" target="true" />
</item>'
Element='<context key="c" target="true" />'

Ancestor and Ancestor-or-self are the XPath Axes. An axis is a node-set relative to the current node.
The ancestor axis selects all the ancestors, i.e. parent, grandparent, etc of the current node whereas the ancestor-or-self selects all the ancestors, i.e. parent, grandparent, etc. of the current node and the current node itself.
Ancestor-or-self is generally used to locate the XML document tags or in XSLT that is a transformation language for XML designed to transform structured documents into other formats (such as XML, HTML, and plain text).
I believe that you may not need these axes to find the XPath in Selenium Webdriver as it deal with HTML tags not the XML tags and there are many other XPath axes that may help in finding the elements.

Related

XSLT: Return the value of a specific child if it is present in one of the parents' in ancestor tree

I have an XML with the structure similar to below.
<root>
<randomElement>..</randomElement>
<EFFECT>..</EFFECT>
<parent2>
<randomElement>..</randomElement>
<EFFECT>..</EFFECT>
<parent>
<randomElement>..</randomElement>
<EFFECT>..</EFFECT>
<randomElement>..</randomElement>
<ITEM>..</ITEM>
</parent>
</parent2>
</root>
Note: there can be any number of <randomElement>s at the places
where it's specified.
So, right now, my pointer is at the <ITEM> tag. I need to return the value inside of the <EFFECT> tag, but, here's the catch.
If it's present, I must return the value of the <EFFECT> tag which is inside <parent> tag. If it's not present there, I must return value of <EFFECT> tag which is inside the <parent2> tag. Again, if it is not present there too, I need to finally return the value of the <EFFECT> tag which is inside <root>. The <EFFECT> inside the <root> will always be present and there can be any number of parents for the <ITEM> element.
Sorry if it's confusing.
To go to any <ITEM>, this is sufficient.
//ITEM
Now, <EFFECT> is a sibling to <ITEM>, i.e. it's on the same level. Another way of thinking about siblings is that they are children of the same parent.
In fact, all <EFFECT> elements in question are children of some ancestor of <ITEM>. This means we can move upwards along the ancestor:: axis and grab all those ancestor elements in one step:
//ITEM/ancestor::*
This will give us <parent>, <parent2> and <root>, in this order.
And from those we only need to take one step down to grab all <EFFECT> elements:
//ITEM/ancestor::*/EFFECT
This will give us three EFFECT elements, this time again in document order (only the ancestor:: type of axis works inside out).
We are interested in the last one of those, because this will be closest to the <ITEM> we started from. The last() function will help here:
(//ITEM/ancestor::*/EFFECT)[last()]
The parentheses around the path are necessary, because otherwise the condition ([last()]) is tested on every <EFFECT> individually, and each one of them is the last one of its kind for its parent, giving us three matches. The parentheses make it so that first a node-set of three <EFFECT> elements is constructed and then the last one is taken, giving us only one match.
When you already currently are at the <ITEM> element, the relative version of the path selects the last effect for that particular item:
(ancestor::*/EFFECT)[last()]

Does node-set() have an ancestor node?

I feel, this is a basic XSLT question. From how I understand, a node-set is data structure in itself. From a node-set() I cannot navigate to any node that is outside of the set. i.e I cannot reach the parent, the beginning of the xml nor its siblings. Is this right?
or
Is there a way to get a parent of a node-set()
Code
<neighbourhood>
<parent name = "xyz">
<child address=10> a </child>
<child address=10> b </child>
<child address=15> c </child>
</parent>
</neighbourhood>
I have a set of child nodes. I need to eliminate nodes with duplicate 'address'.
There can be n number of 'parent' and m number of 'child' and there could be a grandparent node for the 'child'.
The best and the logical way is probably go by each parent and process their children. But, it is an existing code base and this is an oversimplified example. I do not want to break too many things by touching the caller function and other templates.
So, my question was if I could get the 'parent' with the set of 'child' nodes I have
Thanks for all your responses
Does node-set() have an ancestor node?
node-set() is a (extension) function -- not a node. A function cannot have ancestor, because it is not a node.
I suppose that by "node-set()" in the question, you mean the value that is returned by the xxx:node-set() function (where the prefix "xxx" is bound to a vendor-specific namespace). If so, here is the wanted answer:
By definition, the xxx:node-set() function returns the document-node() (also known as root-node in XPath 1.0) of a temporary tree, which is obtained by converting the RTF (Result Tree Fragment), passed as the only argument to this function.
A document-node by definition is at the top of the document hierarchy and is the only node in an XML document, that doesn't have a parent.
Therefore, the node returned by a called xxx:node-set() function doesn't have any ancestors.
From a node-set() I cannot navigate to any node that is outside of the set. i.e I cannot reach the parent, the beginning of the xml nor its siblings. Is this right?
Yes, without calling another function that returns a node from another document (such as the standard XPath function id() or the standard XSLT function document()), or referencing a variable/parameter, it is not possible to navigate to a node from another document only by using XPath location steps.
or
Is there a way to get a parent of a node-set()
No, the node returned by the xxx:node-set() function is a document node and a document node doesn't have a parent (or any other ancestor) node.
Don't mix up node sets and node-set()s.
What do I mean by this? Well, a node set is a set of nodes. In normal, unextended XSLT 1.0, this means a selection of nodes from your input document. If I do this:
<!-- a node set -->
<xsl:variable name="my-node-set"
select="/indoc/level1/level2"/>
the variable $my-node-set contains a set of level2 nodes, but those nodes still live in the input document. If I subsequently do a for-each like so:
<nodeset-from-indoc>
<xsl:for-each select="$my-node-set/level3">
<parent>
<xsl:value-of select="local-name(..)"/>
</parent>
<grandparent>
<xsl:value-of select="local-name(../..)"/>
</grandparent>
</xsl:for-each>
</nodeset-from-indoc>
I will get the names of the parents and grandparents of each node:
<nodeset-from-indoc>
<parent>level2</parent><grandparent>level1</grandparent>
<parent>level2</parent><grandparent>level1</grandparent>
<parent>level2</parent><grandparent>level1</grandparent>
</nodeset-from-indoc>
If, however, I hard-code nodes into a variable:
<!-- a result-tree fragment -->
<xsl:variable name="my-rtf">
<level2>
<level3>1</level3>
</level2>
<level2>
<level3>2</level3>
</level2>
<level2>
<level3>3</level3>
</level2>
</xsl:variable>
this is not a node set, but a result tree fragment, since they weren't selected from the input document. The problem with result tree fragments is that you can't use XPath on them. I can't, for example, do this:
<xsl:for-each select="$my-rtf/level3">
This is where the node-set() function comes in. It is an extension to XSLT 1.0, which comes from some extension namespace, depending on your XSLT processor. Many processors choose to implement this in the namespace defined by EXSLT.
As Dmitre points out, the node-set() function returns a magic document node of a temporary tree, allowing you to use XPath. However, this causes a subtle shift in how the select needs to be done. Because of the magic document node, I have to include level2 in my selection:
<nodeset-from-rtf>
<xsl:for-each select="exsl:node-set($my-rtf)/level2/level3">
<parent>
<xsl:value-of select="local-name(..)"/>
</parent>
<grandparent>
<xsl:value-of select="local-name(../..)"/>
</grandparent>
</xsl:for-each>
</nodeset-from-rtf>
And in this case, the level3 nodes will have parents, but no grandparents:
<nodeset-from-rtf>
<parent>level2</parent><grandparent/>
<parent>level2</parent><grandparent/>
<parent>level2</parent><grandparent/>
</nodeset-from-rtf>
A node-set is a set of nodes. Each node in the node-set has ancestors. The node-set itself does not. If $NS is a node-set, you can do $NS/ancestor::node(): this will give you all the ancestors of all the nodes in the node-set, with duplicates eliminated.

For-each loop in JDeveloper doesn't map the response at all

I'm developing a BPEL service using JDeveloper 11.1.1.5.0.
The API's response contains some recurring fields and I'm trying to use a for-each loop to map them with the final web-service's response parameters.
The API's response structure is like this :
<Data>
<Item>
<F6181_SubsWalletCounter>-1</F6181_SubsWalletCounter>
<FBalanceExpDate>2013-08-13T00:00:00</FBalanceExpDate>
<FResetWalletCounterValue>0</FResetWalletCounterValue>
<FRecurringPeriod>0</FRecurringPeriod>
<FRecurringRefreshDate>1899-12-30T00:00:00</FRecurringRefreshDate>
<FRecurringRefreshDay>0</FRecurringRefreshDay>
<F6150_AccountProfileId>18</F6150_AccountProfileId>
<FLimit>0</FLimit>
<F8345_PaymentDebt>0</F8345_PaymentDebt>
<F9217_MinBalance>0</F9217_MinBalance>
<F9218_MaxPaymentDebt>-1</F9218_MaxPaymentDebt>
</Item>
<Item>
<F6181_SubsWalletCounter>-1</F6181_SubsWalletCounter>
<FBalanceExpDate>2013-08-13T00:00:00</FBalanceExpDate>
<FResetWalletCounterValue>0</FResetWalletCounterValue>
<FRecurringPeriod>0</FRecurringPeriod>
<FRecurringRefreshDate>1899-12-30T00:00:00</FRecurringRefreshDate>
<FRecurringRefreshDay>0</FRecurringRefreshDay>
<F6150_AccountProfileId>18</F6150_AccountProfileId>
<FLimit>0</FLimit>
<F8345_PaymentDebt>0</F8345_PaymentDebt>
<F9217_MinBalance>0</F9217_MinBalance>
<F9218_MaxPaymentDebt>-1</F9218_MaxPaymentDebt>
</Item>
</Data>
The <Item> element and its sub-elements could repeat multiple times maintaining the exact structure within itself. Out of these sub elements, few are to be mapped with the final response parameters.
The xsl code is :
<ns2:responseBody>
<ns2:balanceInfo>
<ns2:balance>
<xsl:for-each select=
"/tns:Subscriber_WalletInfo_GetResponse/Data/Item">
<ns2:wallet>
<xsl:value-of select="F6091_WalletTypeName"/>
</ns2:wallet>
</xsl:for-each>
<ns2:expirationDate>
<xsl:value-of select="FBalanceExpDate"/>
</ns2:expirationDate>
<ns2:balanceAmount>
<xsl:value-of select="F9261_Balance"/>
</ns2:balanceAmount>
<ns2:unit>
<xsl:value-of select="F8341_CurrencyName"/>
</ns2:unit>
</ns2:balance>
</ns2:balanceInfo>
<ns3:Error>
<ns3:description>
<xsl:value-of select="/tns:Subscriber_WalletInfo_GetResponse/ErrorDescription"/>
</ns3:description>
</ns3:Error>
</ns2:responseBody>
But the response doesn't get mapped with this xsl. I'm not sure if I've made any syntactical error or if any namespace is missing.
Any kind of guidance would be great!
I'm not sure if I've made any syntactical error or if any namespace is missing.
Right both times, I think.
First, the syntax errors.
If the XML introduced with the words "The xsl code is" is in fact the entire stylesheet, then your first syntactic error is that your stylesheet is not namespace-well-formed: you have not declared any of the namespace prefixs ns2, ns3, xsl.
Your second syntactic error is at the XSLT level: the namespace prefix tns is also not declared.
Your third syntactic error is that the outermost element does not include any xsl:version attribute.
When I supply dummy namespace declarations for the unbound prefixes (and http://www.w3.org/1999/XSL/Transform for the prefix xsl) and supply an xsl:version="1.0" attribute on the outermost element, then the stylesheet becomes a legal XSLT 1.0 stylesheet, using the "literal result element as stylesheet" option.
So much for the syntax errors. When I run the repaired stylesheet on the input you supply, I get output. Not the output you want, but it's progress:
<?xml version="1.0"?>
<ns2:responseBody xmlns:ns2="http://example.com/ns2"
xmlns:ns3="http://example.com/ns3"
xmlns:tns="http://example.com/tns">
<ns2:balanceInfo>
<ns2:balance>
<ns2:expirationDate/>
<ns2:balanceAmount/>
<ns2:unit/>
</ns2:balance>
</ns2:balanceInfo>
<ns3:Error>
<ns3:description/>
</ns3:Error>
</ns2:responseBody>
Now the semantic errors, which include but are not limited to a namespace error.
You're not getting any information about your input here, because the select expressions in your stylesheet (specifically /tns:Subscriber_WalletInfo_GetResponse/Data/Item and /tns:Subscriber_WalletInfo_GetResponse/ErrorDescription) aren't matching anything. Both of these path expressions say, roughly:
Start at the root of the document (that's the /).
The outermost element will be named Subscriber_WalletInfo_GetResponse, in the namespace bound to prefix tns.
Within that element, the second select expression searches for a child named ErrorDescription which is not namespace-qualified. The first expression searches for a child (or: the set of all children) named Data, and then within the Data element(s), the set of all children named Item, and evaluates the contents of the for-each instruction once for each of them. The elements Item and Data are both expected to be namespace-unqualified.
In the data you show, by contrast, the outermost element is not named Subscriber_WalletInfo_GetResponse and is not namespace-qualified. Naturally, neither select expression doesn't match anything: they both fail at step 2.

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.

How to find ancestor-or-self that is a child of an element with particular attribute?

I'm working with a very generic XML structure, where everything is an item (well everything relevant to this question anyway).
Based on knowing the item element I'm currently on and the item element that is the parent of the node I'm looking for, I need to find an item. I have a working xpath, but it's fairly resource intensive and I'm looking for something more elegant and cheaper.
The item key=a node is the parent of the element I'm looking for (though it's not actually a child of the document root)
XML:
<root>
<item key="a">
<item key="b">
<item key="c">
<item key="d"/>
</item>
</item>
<item key="e">
<item key="f">
<item key="g"/>
</item>
</item>
</item>
</root>
The actual XML is much deeper and with far more branching.
So for instance, if I'm on the item with key=g, e or f I need to return the item with key=e. If I'm on the item with key b,c or d I need to return the item with key=b.
I'm using this xpath, which is working, but going up and then back down the ancestor-descendant axis seems a far longer trip than I need.
current()
/ancestor-or-self::item[#key='a']
/item[descendant-or-self::* = current()]
Is there a simpler way of doing this, bearing in mind that I only know 1) the node I'm on and 2) the key attribute of the parent of the node I'm looking for?
Just for detail's sake: The XML is Sitecore generated, I'm not actually using the current() function, I'm using the sc_currentitem parameter to set the start node I need to begin processing at.
Thanks in advance.
Use:
ancestor-or-self::*[parent::item[#key='a']]