Comparing two nodes in XPath - xpath

I am writing an Xpath Query to be used as a rule in PMD.
Now
//Method/ModifierNode[Annotation[#Image = 'Future']]/..[#Image = 'randomMethod']]
gives me one node and
//ForEachStatement
//MethodCallExpression
[#MethodName = 'randomMethod']
gives me another.
I want to compare these two and see whether the name of the node in the first query and the name of the node in the second query are same or not.
I am doing this
//ForEachStatement
//MethodCallExpression
[#MethodName = //Method/ModifierNode[Annotation[#Image = 'Future']]/..[#Image]]
This is not working at all and is returning zero matched nodes.

You have an issue with the types of values you are comparing
#MethodName is a string.
//Method/ModifierNode[Annotation[#Image = 'Future']]/..[#Image] selects a node (making sure it has a non-empty image).
So when comparing both, it will always be false. You want to get the name of the method node in the second selector, so you can compare strings. You can do so by doing…
//Method/ModifierNode[Annotation[#Image = 'Future']]/../#Image
So your XPath should look like
//ForEachStatement
//MethodCallExpression
[#MethodName = //Method/ModifierNode[Annotation[#Image = 'Future']]/../#Image]

Related

Need some explanation about getting max in XPath

I'm kinda new to XPath and I've found that to get the max attribute number I can use the next statement: //Book[not(#id > //Book/#id) and it works quite well.
I just can't understand why does it return max id instead of min id, because it looks like I'm checking whether id of a node greater than any other nodes ids and then return a Book where it's not.
I'm probably stupid, but, please, someone, explain :)
You're not querying for maximum values, but for minimum values. Your query
//Book[not(#id > //Book/#id)
could be translated to natural language as "Find all books, which do not have an #id that is larger than any other book's #id". You probably want to use
//Book[not(#id < //Book/#id)
For arbitrary input you might have wanted to use <= instead, so it only returns a single maximum value (or none if it is shared). As #ids must be unique, this does not matter here.
Be aware that //Book[#id > //Book/#id] is not equal to the query above, although math would suggest so. XPath's comparison operators adhere to a kind of set-semantics: if any value on the left side is larger than any value on the right side, the predicate would be true; thus it would include all books but the one with minimum #id value.
Besides XPath 1.0 your function is correct, in XPath 2.0:
/Books/Book[id = max(../Book/id)]
The math:max function returns the maximum value of the nodes passed as the argument. The maximum value is defined as follows. The node set passed as an argument is sorted in descending order as it would be by xsl:sort with a data type of number. The maximum is the result of converting the string value of the first node in this sorted list to a number using the number function.
If the node set is empty, or if the result of converting the string values of any of the nodes to a number is NaN, then NaN is returned.
The math:max template returns a result tree fragment whose string value is the result of turning the number returned by the function into a string.

How to parse a certain type of configuration file

Recently I encounter an interview question. I was required to write code for expression evaluation. The expression format looks like this:
B=10;
A={
A=100;
B=BDE;
C=C;
D={
A=windows;
B=mac;
C={
A=redhat;
B=ubuntu;
};
};
A+={
A=200;
E=1000;
};
To represent the key of the expression, period delimitated method is used. For example, A.B represents the element B in Map A, and the value of A.B is BDE; similarly, the value of A.D.C.A is redhat. the the represent string is called 'path expression'.
the configuration also support append and override operation. for the above example, we use += operation to append or override the value in Map A. now the value of A.A is 200, and the value of A.E is 1000;
Now, given a configuration strings and the key path of configuration, I was required to return the value of configuration based the configuration strings.
Rules
1) the key name and his value only contains alphabet(A-Z,a-z) and number(0-9), no other characters;
2) if cannot find the value or the expression point to a map, please output "N/A"
3) if find the value, please output the value. no spaces when output the value.
Input and Output
there are three part sin the input. the first line contains two integers indicates the number of congiruation lines(M) and the number of expressions(N).
M<=100, N<=100. the following M lines are the confugration and the last N lines are expression. every configuration line contains one or more configurations. every line length less than 1000.
Input :
2 2
A={A=1;B=2;C=3;E={A=100;};};
A+={D=4;E={B=10;C=D;};};
A.E.B
B.D.E
Output
A.E.B=10
B.D.E=N/A
My thoughts
I was thinking about using a N-nary tree to represent the expression. For example, the expression: A = {A = 1;D = 1;B = {C = 1,D = {D = 1,F = 2};};}; can be represented as:
(A,-)
/ | \
(A,1) (D,1) (B,-)
/ \
(C,1) (D,-)
/ \
(D,1) (F,2)
Since a N-nary tree can be represented as a binary tree. Thus, all append or search operations would be either the insert or search operations for a binary tree. It seems that this approach works. But I am wondering if there is a better way to approach this problem?
I am thinking about putting all children in a hash map (since that's what interviewers like)
Node{
String val;
HashMap<String, Node> children;
Node(int val){
this.val = val;
children = new HashMap<String, Node>();
}
}

XPath :: running counter two levels

Using the count(preceding-sibling::*) XPath expression one can obtaining incrementing counters. However, can the same also be accomplished in a two-levels deep sequence?
example XML instance
<grandfather>
<father>
<child>a</child>
</father>
<father>
<child>b</child>
<child>c</child>
</father>
</grandfather>
code (with Saxon HE 9.4 jar on the CLASSPATH for XPath 2.0 features)
Trying to get an counter sequence of 1,2 and 3 for the three child nodes with different kinds of XPath expressions:
XPathExpression expr = xpath.compile("/grandfather/father/child");
NodeList nodes = (NodeList) expr.evaluate(doc, XPathConstants.NODESET);
for (int i = 0 ; i < nodes.getLength() ; i++) {
Node node = nodes.item(i);
System.out.printf("child's index is: %s %s %s, name is: %s\n"
,xpath.compile("count(preceding-sibling::*)").evaluate(node)
,xpath.compile("count(preceding-sibling::child)").evaluate(node)
,xpath.compile("//child/position()").evaluate(doc)
,xpath.compile(".").evaluate(node));
}
The above code prints:
child's index is: 0 0 1, name is: a
child's index is: 0 0 1, name is: b
child's index is: 1 1 1, name is: c
None of the three XPaths I tried managed to produce the correct sequence: 1,2,3. Clearly it can trivially be done using the i loop variable but I want to accomplish it with XPath if possible. Also I need to keep the basic framework of evaluating an XPath expression to get all the nodes to visit and then iterating on that set since that's the way the real application I work on is structured. Basically I visit each node and then need to evaluate a number of XPath expressions on it (node) or on the document (doc); one of these XPAth expressions is supposed to produce this incrementing sequence.
Use the preceding axis with a name test instead.
count(preceding::child)
Using XPath 2.0, there is a much better way to do this. Fetch all <child/> nodes and use the position() function to get the index:
//child/concat("child's index is: ", position(), ", name is: ", text())
You don't say efficiency is important, but I really hate to see this done with O(n^2) code! Jens' solution shows how to do that if you can use the result in the form of a sequence of (position, name) pairs. You could also return an alternating sequence of strings and numbers using //child/(string(.), position()): though you would then want to use the s9api API rather than JAXP, because JAXP can only really handle the data types that arise in XPath 1.0.
If you need to compute the index of each node as part of other processing, it might still be worth computing the index for every node in a single initial pass, and then looking it up in a table. But if you're doing that, the simplest way is surely to iterate over the result of //child and build a map from nodes to the sequence number in the iteration.

How to select all nodes such that their group size is higher than a given value, in XPath

I would like to select all <mynode> elements that have a value that appears a certain number of times (say, x) in all the elements.
Example:
<root>
<mynode>
<attr1>value_1</attr1>
<attr2>value_2</attr2>
</mynode>
<mynode>
<attr1>value_3</attr1>
<attr2>value_3</attr2>
</mynode>
<mynode>
<attr1>value_4</attr1>
<attr2>value_5</attr2>
</mynode>
<mynode>
<attr1>value_6</attr1>
<attr2>value_5</attr2>
</mynode>
</root>
In this case, I want all the <mynode> elements that whose attr2 value occurs > 1 time (x = 1). So, the last two <mynode>s.
Which query I have to perform in order to achieve this target?
If you're using XPath 2.0 or greater, then the following will work:
for $value in distinct-values(/root/mynode/attr2)
return
if (count(/root/mynode[attr2 = $value]) > 1) then
/root/mynode[attr2 = $value]
else ()
For a more detailed discussion see: XPath/XSLT nested predicates: how to get the context of outer predicate?
This is also possible in plain XPath 1.0 (also works in newer versions of XPath); and probably easier to read. Think of your problem as you're looking for all <mynode/>s which have an <att2/> node that also occurs before or after the <mynode/>:
//mynode[attr2 = preceding::attr2 or attr2 = following::attr2]
If <att2/> nodes can also accour inside other elements and you do not want to test for those:
//mynode[attr2 = preceding::mynode/attr2 or attr2 = following::mynode/attr2]

How to get first two occurrences using LINQ?

I need to know the number of elements that satisfy a condition so I perform the following:
int numberOfItems = context.SomeEntity.Count(someCondition);
but as I only need to check if, and only if, numberOfItems is exactly 1, I would like to improve this query and be more efficient by stopping counting items when first two occurrences satisfy the condition (when first 2 occurrences are encountered that satisfy the condition, there is no need to continue checking it). Performing something like:
bool existsOnlyOne = context.SomeEntity....
How to achieve this?
You can change the condition to
bool existsOnlyOne = context.SomeEntity.Where(someCondition).Take(2).Count() == 1;
If you have more than two items, the items from the third one on would be ignored by the Take(2) method.

Resources