How to Convert XPath Nodelist Attributes to Strings - xpath

I have an XPath expression that returns the following items but need to get the values from them. (respectively "2" & "3" but entire attribute would work too)
(my xml and path expression is here: Getting Upper XPath Value Based on Lower XML value)
Attribute='SERepeatKey =2'
Attribute='SERepeatKey =3'
This doesn't work
NodeList nl = (NodeList) xPath.compile(strXPath).evaluate(docODM, XPathConstants.NODESET);
for (int x = 0; x < nl.getLength(); x++){
Node ser = nl.item(x);
String sSer = ser.getAttributes().getNamedItem("SERepeatKey").getNodeValue().toString();
}

I removed the #SERepeatKey from the previous XPath statement to return the entire xml nodes then the above statement worked returning the "2" and "3" values.
<Data SOID="(DEV)" MetaDataVersionOID="" >
<SData SKey="003-003" >
<SiteRef LOID="SE 003"/>
<SEData SEOID="UNSCHEDULED" SERepeatKey="2">
<FData FOID="REQUIRED" FormRepeatKey="1">
<IGData IGOID="REQUIRED" IGRepeatKey="0" TransactionType="Upsert">
<IData ItemOID="REQUIRED.REQ_V" Value="1" />
</IGData>
</FData>
</SEData>
</SData>
</Data>
<Data SOID="(DEV)" MetaDataVersionOID="" >
<SData SKey="003-003" >
<SiteRef LOID="SE 003"/>
<SEData SEOID="UNSCHEDULED" SERepeatKey="3">
<FData FOID="REQUIRED" FormRepeatKey="1">
<IGData IGOID="REQUIRED" IGRepeatKey="0" TransactionType="Upsert">
<IData ItemOID="REQUIRED.REQ_V" Value="1" />
</IGData>
</FData>
</SEData>
</SData>

Related

XPath Param Value Is Not Evaluating In Expression

The #Value='1' Does not seem to be evaluating in this expression below as it returns both nodes of SEventData instead of only one that has a #Value of '1'. What am I missing?
Source:
<CData>
<SData SKey="006" >
<SEventData SEventOID="UNS" SEventRepeatKey="2">
<FData FOID="REQUIRED" FRepeatKey="1">
<ItemGroupData ItemGroupOID="REQUIRED" ItemGroupRepeatKey="0"
TransactionType="Upsert">
<ItemData ItemOID="REQ" Value="0" />
</ItemGroupData>
</FData>
</SEventData>
<SEventData SEventOID="UNS" SEventRepeatKey="3">
<FData FOID="REQUIRED" FRepeatKey="1">
<ItemGroupData ItemGroupOID="REQUIRED" ItemGroupRepeatKey="0"
TransactionType="Upsert">
<ItemData ItemOID="REQ" Value="1" />
</ItemGroupData>
</FData>
</SEventData>
</SData>
</CData>
with expression
//CData[.//SData[#SKey='006']/SEventData/FData/ItemGroupData/ItemData[#ItemOID='REQ' and #Value='1']]//SEventData
returns both instead of one
Element='<SEventData SEventOID="UNS" SEventRepeatKey="2">
<FData FOID="REQUIRED" FRepeatKey="1">
<ItemGroupData ItemGroupOID="REQUIRED"
ItemGroupRepeatKey="0"
TransactionType="Upsert">
<ItemData ItemOID="REQ" Value="0"/>
</ItemGroupData>
</FData>
</SEventData>'
Element='<SEventData SEventOID="UNS" SEventRepeatKey="3">
<FData FOID="REQUIRED" FRepeatKey="1">
<ItemGroupData ItemGroupOID="REQUIRED"
ItemGroupRepeatKey="0"
TransactionType="Upsert">
<ItemData ItemOID="REQ" Value="1"/>
</ItemGroupData>
</FData>
</SEventData>'
You can use this XPath in order to locate what you are looking for:
//SEventData[.//FData/ItemGroupData/ItemData[#ItemOID='REQ' and #Value='1']]
Your current XPath expression returns the root node //CData containing in it the following condition: .//SData[#SKey='006']/SEventData/FData/ItemGroupData/ItemData[#ItemOID='REQ' and #Value='1'].
So yes, //CData has inside it the condition as above and when you trying to locate //SEventData inside that //CData it returns you both //SEventData nodes.
What you should do here is to locate //SEventData node itself based on your condition as I wrote at the beginning:
//SEventData[.//FData/ItemGroupData/ItemData[#ItemOID='REQ' and #Value='1']]

Getting Upper XPath Value Based on Lower XML value

I'm trying to get the value of SERepeatKey if ItemOID REQUIRED.REQ_V has a value of "1".
In the below case I want to return the value 2.
Is this possible with this XML? If so how?
With this I was able to do it if there was only 1 "Data" node but now with 2 I am at a loss. In XPath tester I get the below error message.
string(ODM/Data/SData[#SKey='003-003' and SiteRef[#LOID='SE 003']]/SEData[#SEOID='UNSCHEDULED']/#SERepeatKey)
"Unable to perform XPath operation. A sequence of more than one item is not allowed as the first argument of string() (#SERepeatKey, #SERepeatKey)"
<ODM>
<Data SOID="(DEV)" MetaDataVersionOID="" >
<SData SKey="003-003" >
<SiteRef LOID="SE 003"/>
<SEData SEOID="UNSCHEDULED" SERepeatKey="1">
<FData FOID="REQUIRED" FormRepeatKey="1">
<IGData IGOID="REQUIRED" IGRepeatKey="0" TransactionType="Upsert">
<IData ItemOID="REQUIRED.REQ_V" Value="0" />
</IGData>
</FData>
</SEData>
</SData>
</Data>
<Data SOID="(DEV)" MetaDataVersionOID="" >
<SData SKey="003-003" >
<SiteRef LOID="SE 003"/>
<SEData SEOID="UNSCHEDULED" SERepeatKey="2">
<FData FOID="REQUIRED" FormRepeatKey="1">
<IGData IGOID="REQUIRED" IGRepeatKey="0" TransactionType="Upsert">
<IData ItemOID="REQUIRED.REQ_V" Value="1" />
</IGData>
</FData>
</SEData>
</SData>
</Data>
<Data SOID="(DEV)" MetaDataVersionOID="" >
<SData SKey="003-003" >
<SiteRef LOID="SE 003"/>
<SEData SEOID="UNSCHEDULED" SERepeatKey="3">
<FData FOID="REQUIRED" FormRepeatKey="1">
<IGData IGOID="REQUIRED" IGRepeatKey="0" TransactionType="Upsert">
<IData ItemOID="REQUIRED.REQ_V" Value="1" />
</IGData>
</FData>
</SEData>
</SData>
This XPath should do what you are looking for:
//Data[.//IData[#ItemOID='REQUIRED.REQ_V' and #Value='1']]//SEData/#SERepeatKey
According to the presented XML looks like you can even omit #ItemOID='REQUIRED.REQ_V' and make this expression simpler
//Data[.//IData[#Value='1']]//SEData/#SERepeatKey

How can I use XPath to find the minimum/maximum value of an attribute for chooses group in a set of elements?

<foo>
<bar id="1" score="100" group="beginner" />
<bar id="2" score="200" group="beginner" />
<bar id="3" score="300" group="expert" />
...
</foo>
I try use like this, but something wrong (xpath 1.0)
foo/bar[#group='beginner' and not(#score<= preceding-sibling::bar/#score) and not(#score<=following-sibling::bar/#score)]
using xpath 1.0
/foo/bar[#group='beginner'][(not(preceding-sibling::bar[#group='beginner']/#score >= #score) and not(following-sibling::bar[#group='beginner']/#score > #score)) or (not(preceding-sibling::bar[#group='beginner']/#score <= #score) and not(following-sibling::bar[#group='beginner']/#score < #score))]/#score

Summary table with xForms

I have an xml like the following:
<table1>
<row>
<person>person1</person>
<value>10</value>
</row>
<row>
<person>person2</person>
<value>20</value>
</row>
<row>
<person>person1</person>
<value>5</value>
</row>
</table1>
<summaryTable>
<row>
<person>person1</person>
<value_total/>
</row>
<row>
<person>person2</person>
<value_total/>
</row>
</summaryTable>
With XForms 1 (there is no option to switch to XForms 2), using framework betterform, I want to calculate the values in the summary table, by doing the SUM of the rows in 'table1' that have the same person name. To do that I have the following binds:
<xf:bind id="bind_table1"
nodeset="table1" repeatableElement="row">
<xf:bind id="bind_head_table1" nodeset="head" />
<xf:bind id="bind_row_table1" nodeset="row">
<xf:bind id="bind_person" nodeset="person" type="xf:string" />
<xf:bind id="bind_value" nodeset="value" type="xf:integer" />
</xf:bind>
</xf:bind>
<xf:bind id="bind_summaryTable"
nodeset="summaryTable"
repeatableElement="row">
<xf:bind id="bind_head_summaryTable" nodeset="head" />
<xf:bind id="bind_row_summaryTable" nodeset="row">
<xf:bind id="bind_person_name" nodeset="person_name" type="xf:string" readonly="true"/>
<xf:bind id="bind_value_total" nodeset="value_total" type="xf:integer" readonly="true" calculate="SUM(//table1/row[person/text() = ../person_name/text()]/value)"/>
</xf:bind>
</xf:bind>
What I want to have at the end is the value_total for person1 = 15 and value_total for person2 = 20, but using this 'calculate' expression I'm getting 'NaN'. If I replace the calculate expression to compare with a literal String like:
<xf:bind id="bind_value_total" nodeset="value_total" type="xf:integer" readonly="true" calculate="SUM(//table1/row[person/text() = 'person1']/value)"/>
then I get as value_total 15 (the sum is correctly done). So it seems that the error is in the comparison expression person/text() = ../person_name/text() . Does someone have an idea about how should be the correct expression?
Thanks
Try the context() function in the calculate attribute to refer to the current node, like this:
<xf:bind nodeset="summaryTable/row/value_total" calculate="sum(//table1/row[person/text() = context()/../person/text()]/value)"/>
The context function gives you the current context node. If your bind references a nodeset with multiple nodes, it will be evaluated one time for every node, and that node is what context() returns.
It works for me with XSLTForms, maybe your version of betterForm supports it.

XPath-1.0: mark node where opposite node is missing

I try to write custom rule in sonar (plsql) by copy xpath rule. The task of this rule is to mark 'rollback to savepoint' statement where savepoint statement is missing.
For 2 savepoint and 3 rollback statements the AST (Abstract Syntax Tree) looks like:
<PROCEDURE_DEFINITION>
...
<SAVEPOINT>
<IDENTIFIER tokenValue="SAVEPOINT" />
<IDENTIFIER tokenValue="spA" />
<SEMICOLON tokenValue=";" />
</SAVEPOINT>
...
<SAVEPOINT>
<IDENTIFIER tokenValue="SAVEPOINT" />
<IDENTIFIER tokenValue="spB" />
<SEMICOLON tokenValue=";" />
</SAVEPOINT>
...
<ROLLBACK>
<IDENTIFIER tokenValue="ROLLBACK" />
<TO />
<IDENTIFIER tokenValue="SAVEPOINT" />
<IDENTIFIER tokenValue="spB" />
<SEMICOLON tokenValue=";" />
</ROLLBACK>
...
<ROLLBACK>
<IDENTIFIER tokenValue="ROLLBACK" />
<TO />
<IDENTIFIER tokenValue="SAVEPOINT" />
<IDENTIFIER tokenValue="spA" />
<SEMICOLON tokenValue=";" />
</ROLLBACK>
...
<ROLLBACK>
<IDENTIFIER tokenValue="ROLLBACK" />
<TO />
<IDENTIFIER tokenValue="SAVEPOINT" />
<IDENTIFIER tokenValue="spX" />
<SEMICOLON tokenValue=";" />
</ROLLBACK>
...
</PROCEDURE_DEFINITION>
I search for XPath query that marks last rollback because savepoint spX is missing.
But this xpath marks last both rollbacks
//PROCEDURE_DEFINITION//ROLLBACK[
IDENTIFIER[
#tokenValue =
./ancestor::PROCEDURE_DEFINITION
//SAVEPOINT/IDENTIFIER[2]/#tokenValue
]
]
Any suggestions?
EDIT:
I found this suboptimal solution:
//PROCEDURE_DEFINITION//ROLLBACK
[
not(
./IDENTIFIER[3]/#tokenValue =
ancestor::PROCEDURE_DEFINITION//SAVEPOINT/IDENTIFIER[2]/#tokenValue
)
]
PLSQL is case insensitive. But when i add translate function, i get marked first and last ROLLBACK node. I think all rollback's names will agreed with first savepoint's name?
First of all, according to the AST you show, it seems to me that this is the PL/SQL code you want to run your XPath query on:
DECLARE
PROCEDURE foo AS
BEGIN
SAVEPOINT spA;
SAVEPOINT spB;
ROLLBACK spB; -- Compliant
ROLLBACK spA; -- Compliant
ROLLBACK spX; -- Non-Compliant
END;
BEGIN
NULL;
END;
/
When I run your first XPath query using the SSLR PL/SQL Toolkit, the rollbacks to 'spB' and 'spA' are selected, but not the one to 'spX'.
Your second XPath query selects all of them.
It seems to me that you only want to select the one to 'spX', as there is no corresponding savepoint.
A trivial change to your first query allows to reverse the selected nodes, by negating the condition using not():
//PROCEDURE_DEFINITION//ROLLBACK[
not(IDENTIFIER[
#tokenValue =
./ancestor::PROCEDURE_DEFINITION
//SAVEPOINT/IDENTIFIER[2]/#tokenValue
])
]
But I would actually recommend that you drop the PROCEDURE_DEFINITION part of the query, as SAVEPOINT and ROLLBACK statements are also valid within functions or anonymous blocks:
//ROLLBACK[not(IDENTIFIER[#tokenValue = //SAVEPOINT/IDENTIFIER[2]/#tokenValue])]

Resources