how i can put two filter conditions where name='SHIPMENT.ORDERS.ORREFNUMS.REFNUM_VALUE' and value node contains ":" in the value - xpath

i want to filter where name=SHIPMENT.ORDERS.ORREFNUMS.REFNUM_VALUE and contains ":" in the value.
<inputData name="SHIPMENT.ORDERS.ORREFNUMS.REFNUM_VALUE" xmlns="http://xmlns.oracle.com/apps/otm/ExternalRating">
<values xmlns="http://xmlns.oracle.com/apps/otm/ExternalRating">
<value xmlns="http://xmlns.oracle.com/apps/otm/ExternalRating">24-100</value>
<value xmlns="http://xmlns.oracle.com/apps/otm/ExternalRating">RC3.B726030-001-CO8MODF183896704</value>
<value xmlns="http://xmlns.oracle.com/apps/otm/ExternalRating">Y</value>
<value xmlns="http://xmlns.oracle.com/apps/otm/ExternalRating">2022-06-23 23:59:59</value>
</values>
</inputData>

Depending on the actual structure of <values> you could, as specified in your question, use:
//inputData[#name="SHIPMENT.ORDERS.ORREFNUMS.REFNUM_VALUE"]//value[contains(.,":")]/text()
If the datetime element is always the 4th <value> you can try:
//inputData[#name="SHIPMENT.ORDERS.ORREFNUMS.REFNUM_VALUE"]//value[4]/text()
or, if it's always the last <value>:
//inputData[#name="SHIPMENT.ORDERS.ORREFNUMS.REFNUM_VALUE"]//value[last()]/text()

Related

How can I compare map-like elements with XMLUnit

I have below xml data:
Control:
<Data>
<propertyValues>
<propertyName>Name1</propertyName>
<value>
<text>
<value>Value1</value>
</text>
</value>
</propertyValues>
<propertyValues>
<propertyName>Name2</propertyName>
<value>
<text>
<value>Value2</value>
</text>
</value>
</propertyValues>
</Data>
Test:
<Data>
<propertyValues>
<propertyName>Name2</propertyName>
<value>
<text>
<value>Value2</value>
</text>
</value>
</propertyValues>
<propertyValues>
<propertyName>Name1</propertyName>
<value>
<text>
<value>Value1</value>
</text>
</value>
</propertyValues>
</Data>
And I would expect these 2 documents are "same".
How can I config xmlUnit to make it work? (I'm using xmlunit 2.6.3)
Thanks
Leon
This is pretty similar to the running example of the "Selecting Nodes" part of XMLUnit's User Guide.
You need to use an ElementSelector that picks the correct propertyValues element when looking at the list of elements and then decides to compare the elements that contain the same nested text inside the only child element named propertyName. This directly translates into
ElementSelectors.conditionalBuilder()
.whenElementIsNamed("propertyValues")
.thenUse(ElementSelectors.byXPath("./propertyName", ElementSelectors.byNameAndText))
...
and then you need to add whatever other rules are required to make the rest work. Looking at the visible rest of your example there are no ambiguous children and a simple
...
.elseUse(ElementSelectors.byName)
.build();
will do.

contains in xpath only checks first match

Taking this xml piece as example:
<list>
<element>
<title>Element 1</title>
<group>Group 1</group>
</element>
<element>
<title>Element 2</title>
<group>Group 1</group>
</element>
<element>
<title>Element 3</title>
<group>Group 1</group>
<group>Group 2</group>
</element>
<element>
<title>Element 4</title>
<group>Group 2</group>
<group>Group 3</group>
</element>
</list>
To get all groups I use the following xpath:
//group/text()
and it works fine -I remove duplicates later in python using a set as I don't know if I can do it with xpath-. But When I want to get the elements that contain "Group 3" I try the following xpath:
//element[contains(group/text(), "Group 3")]
and I get an empty result. While when I search elements that contain "Group 1" with:
//element[contains(group/text(), "Group 1")]
I get the correct result with 3 elements. And if I look for "Group 2" I get a wrong result with only one element.
What I'm not taking into account? How can I make those searches by group?
contains can only test, if a string occurs in another string. group/text() is not a string, it is a set/sequence of nodes.
You can use
//element[group/text() = "Group 1"]

Get all preceding and following siblings for a given node

I have the following XML snippet:
<root>
<CharacteristicUse>
<UseArea>Specification_Characteristics</UseArea>
<Value>
<ValueID>123</ValueID>
</Value>
<Value>
<ValueID>444</ValueID>
</Value>
<Value>
<ValueID>555</ValueID>
</Value>
<Value>
<ValueID>777</ValueID>
</Value>
<Value>
<ValueID>888</ValueID>
</Value>
</CharacteristicUse>
</root>
I want to be able to get all preceding and following siblings of the Value node which has its child node ValueID with text = 555
I have been trying to combine the following way:
/root/CharacteristicUse/Value[ValueID='555']/following-sibling::* | preceding-sibling::*
But, it only returns to me the following siblings.
Is it possible to have one single query to get the output as:
<Value>
<ValueID>123</ValueID>
</Value>
<Value>
<ValueID>444</ValueID>
</Value>
<Value>
<ValueID>777</ValueID>
</Value>
<Value>
<ValueID>888</ValueID>
</Value>
One way is brute-force:
/root/CharacteristicUse/Value[ValueID='555']/preceding-sibling::* | /root/CharacteristicUse/Value[ValueID='555']/following-sibling::*
This includes the "UseArea" node also, not sure if you want that.
Or if what you want is just all the values that are NOT a particular value, then
/root/CharacteristicUse/Value[not(ValueID='555')]
is more direct.
I found the answer.
The xpath looks like:
/root/CharacteristicUse/Value[ValueID='555']/following-sibling::Value | /root/CharacteristicUse/Value[ValueID='555']/preceding-sibling::Value

XPATH expression that Matches on the attribute value "true"

I have some XML like this:
<engine-set>
<engine host-ref="blah1.com">
<property name="foo" value="true"/>
<property name="bar" value="true"/>
</engine>
<engine host-ref="blah2.com">
<property name="foo" value="true"/>
<property name="bar" value="false"/>
</engine>
</engine-set>
I want to match on all engine elements that have a child node property with a name equal to "bar" and and value equal to "true". I'm finding the fact that "true" appears in my XML is causing my condition to always evaluate to true in an XPath expression. Is there a way around? I'm using Python and lxml.
EDIT:
My xpath expression is (that isn't working) is:
//engine[(property/#name='bar' and property/#value="true")]
Thanks,
I want to match on all engine elements
This is:
//engine
that have a child node property
Now this becomes:
//engine[property]
with a name equal to "bar"
Still more specific:
//engine[property[#name = 'bar']]
and and value equal to "true".
Finally:
//engine[property[#name = 'bar' and #value = 'true']]
So you're saying
//engine[property[#name='bar' and #value='true']]
gives you too many results? Because for me it gives just one.
What XPath expression did you try?
The following seems to work well in getting "blah1.com" but not "blah2.com":
//engine[property[#value="true"][#name="bar"]]
Remember that you need to encase your parameter test values in quotes.

XPath matching attribute and content of an element

Can anyone help with the following XPath question? Given the node-set:
<table>
<rows>
<row>
<value column="Product">Coal</value>
<value column="Quantity">10000</value>
</row>
<row>
<value column="Product">Iron</value>
<value column="Quantity">5000</value>
</row>
<row>
<value column="Product">Ore</value>
<value column="Quantity">4000</value>
</row>
</rows>
</table>
I want to query to find the node sub-set with a given product name. Note that the product name is being supplied by an attribute of the current node being processed (i.e. "#name"). So when the #name attribute has the value of "Coal" I would expect this to be returned:
<row>
<value column="Product">Coal</value>
<value column="Quantity">10000</value>
</row>
This is what I've come up with; I know it's wrong, because I don't get anything back.
$table/rows/row[value[#column='Product'][text()=#name]]
</code>
You are obviously missing the current() function
$table/rows/row[value[#column='Product'] = current()/#name]
Within an XPath predicate (i.e. within square brackets) the context node is the node the predicate is applied to.
In your case, when you say $table/rows/row[x=#name], then #name refers to the #name attribute of row. Which has no #name attribute, so the predicate always evaluates to false for all nodes.
current() returns the current XSLT context node to help in exactly this case.

Resources