xpath to select all leaf nodes with given ancestors attribute - c#

I have a batch of xmls where each tag has an attribute isOptional. An example of such xml is
<node1 isOptional="False">
<node2 isOptional="True">
<node22 isOptional="False">
<node3 isOptional="False">text4</node3>
</node22>
</node2>
<node4 isOptional="False">
<node5 isOptional="False">
<node6 isOptional="True">text3</node6>
<node7 isOptional="False">
<node8 isOptional="True">text2</node8>
<node9 isOptional="False">text1</node9>
</node7>
</node5>
</node4>
</node1>
I need to write xpath to select all leaf nodes which do not have ancessors with #isOptional="True". For this example the result should be:
<node9 isOptional="False">text1</node9>
Actually I need something like this:
//*[not(*) and #isOptional="False" and count(ancestor::*[#isOptional = "True"] = 0)]
Could you please help me to get the correct xpath?

An XPath like this one should work:
//*[not(child::*) and not(ancestor-or-self::*[#isOptional = 'True'])]

I think you forgot to mention one additional requirement. What you said:
I need to write xpath to select all leaf nodes which do not have ancessors with #isOptional="True"
would map to the following XPath expression:
//*[not(*) and not(ancestor::*[#isOptional = 'True'])]
^^^ "all"
^^^^^^^ "leaf nodes"
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ "no ancestor where
#isOptional equals 'True'"
which would yield (individual results separated by ------):
<node6 isOptional="True">text3</node6>
-----------------------
<node8 isOptional="True">text2</node8>
-----------------------
<node9 isOptional="False">text1</node9>
But I assume you meant to add: Additionally, the isOptional attribute of the target nodes must not be set to True either. This results in the following path expression, already correctly suggested by potame:
//*[not(*) and not(ancestor-or-self::*[#isOptional = 'True'])]
and the only result is
<node9 isOptional="False">text1</node9>
because now the XPath expression contains the ancestor-or-self:: axis, which includes the context node itself.

Related

XPath for attribute value that ends with a string?

I'm having trouble selecting an element with an attribute that ends with a certain value.
XML looks like
<root>
<object name="1_2"><attribute name="show" value="example"></object>
<object name="1_1"><attribute name="show" value="example"></object>
<object name="2_1"><attribute name="show" value="example"></object>
</root>
So I need to extract all values from attributes in objects ends with _1, how can I do that?
I did this code
XmlNodeList childnodes = xRoot.SelectNodes("//Object[#Name='1_1']");
foreach (XmlNode n in childnodes)
Console.WriteLine(n.SelectSingleNode("Attribute[#Name='show']").OuterXml);
but I can't find how to search for the part of attributes name and how to get the exact value of target parameter.
First note that XML and XPath are case sensitive, so Object is different than object, and Name is different than name.
XPath 2.0
This XPath 2.0 expression,
//object[ends-with(#name,'_1')]
will select all object elements whose name attribute value ends with _1.
XPath 1.0
XPath 1.0 lacks the ends-with() function but can achieve the same result with a bit more work:
ends-with($s, $e) ≡ (substring($s, string-length($s) - string-length($e) +1) = $e)
Applied to your case where $s is #name and $e is '_1', the above simplifies to this expression:
//object[substring(#name, string-length(#name) - 1) = '_1']
If C# supports XPath 2.0 you should be able to use:
XmlNodeList childnodes = xRoot.SelectNodes("//object[ends-with(#name, '_1')]");
if not then a slightly longer version should work:
XmlNodeList childnodes = xRoot.SelectNodes("//object[substring(#name, string-length(#name) - 1) = '_1']");
Also your xml is not valid as you need to close the attribute elements:
<root>
<object name="1_2"><attribute name="show" value="example"/></object>
<object name="1_1"><attribute name="show" value="example"/></object>
<object name="2_1"><attribute name="show" value="example"/></object>
</root>

Select an XML node with a given child value and update a different child element

I have the following xml file:
<LabelImageCreator xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<PrintFieldList>
<PrintFieldDefinition>
<FieldName>Facility</FieldName>
<DataParameterName>Address</DataParameterName>
<FieldFont>
<FontName>Arial</FontName>
<FontSize>10</FontSize>
<FontStyle>Regular</FontStyle>
</FieldFont>
<CurrentDataValue/>
</PrintFieldDefinition>
<PrintFieldDefinition>
<FieldName>Country</FieldName>
<DataParameterName>CountryofOrigin</DataParameterName>
<WrapField>false</WrapField>
<FieldFont>
<FontName>Arial</FontName>
<FontSize>8</FontSize>
<FontStyle>Regular</FontStyle>
</FieldFont>
<CurrentDataValue/>
<TextPrefix>Produce of </TextPrefix>
</PrintFieldDefinition>
<PrintFieldList>
<LabelImageCreator>
I have to select the attribute with field name Facilityand add the address(eg: No 2546, Gorrge street, California, US) to <CurrentDataValue/> field and save it.
I tried with the below code,
XmlDocument xmlDocument = new XmlDocument();
xmlDocument.Load(path);
var node = xmlDocument.DocumentElement.SelectSingleNode(
"./PrintFieldList/PrintFieldDefinition[#FieldName='Facility']");
Above code while debuging it is not working. Can any one guide me how to select and update the xml attribute.
A couple of minor issues:
You need to start from the root element LabelImageCreator
FieldName is an element, not an attribute, so hence FieldName and not #FieldName
The closing tags on the Xml Document don't match up.
If you want to select the child element CurrentDataValue of parent PrintFieldDefinition with the child FieldName with value Facility:
var node = xmlDocument.DocumentElement.SelectSingleNode(
"/LabelImageCreator/PrintFieldList/PrintFieldDefinition[FieldName='Facility']/CurrentDataValue");
Changing the value is then simply:
node.InnerText = "No 2546, Gorrge street, California, US";
I would use XDocument instead of XmlDocument (it allows you to use linq which in my opinion, is easier than using xpath).
You can find your node like this and I believe you can update them too (first search and get the value, then search again and update on the other node).
Example:
var nodesMatching = from node in myXDocument.Descendants()
where node.Name.LocalName.Equals("mySearchNode")
select node;
var node = nodesMatching.FirstOrDefault();

C#: Get needed nodes using XPath

I have XmlDocument object with next structure:
<ROOT>
<MESSAGE>
<some_fields />
<myDate>
<myParameter>
</MESSAGE>
<MESSAGE>
...
</ROOT>
I want to get all MESSAGE nodes where myDate>= given date and myParameter=given parameter.
I.e. something like:
MyDoc.SelectNodes("/ROOT/MESSAGE..")
Is it possible to do it using XPath?
=================
OK. myDate has already type xs:dateTime. But now I have next exception:
//MESSAGE[myDate < xs:dateTime(2012-06-22T11:17:44)]' has an invalid
qualified name.
Code is:
XmlNodeList nodeList = MyXmlDocument.SelectNodes("//MESSAGE[myDate < xs:dateTime(" + givenDateTime + ")]");
And it doesn't work even with
"//MESSAGE[xs:dateTime('2012-06-22T11:47:32')=xs:dateTime('2012-06-22T11:47:32')]"
Then I have System.Xml.XPath.XPathException:
Namespace Manager or XsltContext needed. This query has a prefix,
variable, or user-defined function.
It can be done purely in XPATH:
MyDoc.SelectNodes("//MESSAGE[xs:date(./myDate#text()) > xs:date('given date') and myParameter[text()='given parameter']]")
[untested]
I would recommend using LINQ.
You could have something like
var nodes = from node in XDoc.Root.Elements("MESSAGE")
where (DateTime)node.Element("myDate") >= DateTime.Now
select node;
This would select the node elements where the myDate time is the current DateTime or in the future. You may want to look further into XML parsing with LINQ. I find it to be the preferred method of XML parsing.
Another example for you:
var nodes = from node in XDoc.Root.Elements("MESSAGE")
where (DateTime)node.Element("myDate") >= DateTime.Now
&& node.Element("myParameter").Value == "whatever"
select node;
Yes, that is very much possible. Please go through the following link where you can get all operators u need.
http://msdn.microsoft.com/en-us/library/aa226440(v=sql.80).aspx
http://www.javabeat.net/2009/03/how-to-query-xml-using-xpath/

Selecting Value from Last Child of an XML file

I am parsing an URI for query_id. I want to get the last query's id. That is actually the last added node in the URI.
I am using the following code but it is failing to return the last node but is returning info from the first node. HELP !!!
XmlDocument doc = new XmlDocument();
doc.Load("helpdesk.hujelabs.com/user.php/1/query");
XmlNode node = doc.DocumentElement;
XmlNode id = node.LastChild.SelectSingleNode("//queries/query/description/text()");
string TID = id.InnerText;
Any answer of the form:
//queries/query[position() = last()]/query_id/text()
or
//queries/query/description[last()]/text()
is wrong.
It is a FAQ: The XPath // pseudo-operator has lower precedence then the [] operator -- this is why the above expressions select any query (or respectively description) element that is the last child of its parent -- these can be all query or description elements.
Solution:
Use:
(//queries/query)[last()]/query_id/text()
Also note: The use of the // pseudo-operator usually results in signifficant loss of efficiency, because this causes the whole (sub) tree rooted at the current node to be completely traversed (O(N^2) operation).
A golden rule: Whenever the structure of the XML document is statically (in advance) known and stable, never use //. Instead use an XPath expression that has a series of specific location steps.
For example, if all the elements you want to select can be selected using:
/x/y/queries/query
then use the above XPath expression -- not //queries/query
use this XPath
//queries/query/description[last()]/text()
To retrieve last query's query_id, change your XPath to
/queries/query[position() = last()]/query_id/text()
Or alternatively, use LINQ to XML:
var doc = XDocument.Load("http://helpdesk.hujelabs.com/user.php/1/query");
var elem = doc.Root.Elements("query").Last().Element("query_id");
var TID = (int)elem;

XmlNode.SelectSingleNode returns element outside current?

my problem is like this. Let's say i have xml like this
<root>
<child Name = "child1">
<element1>Value1</element1>
<element2>Value2</element2>
</child>
<child Name = "child2">
<element1>Value1</element1>
<element2>Value2</element2>
<element3>Value3</element3>
</child>
</root>
I have a method that gets as parameter XmlNode "node". Lets say "node" has value "child1" Then i try like this:
node.SelectSingleNode( "//element3" );
The problem is this code returns element3 from "child2". What i want is if there is no child "element3" of "node" to return null so i add it by hand.
Best Regards,
Iordand
The XPath expression you have isn't what you want.
Replace it with this:
node.SelectSingleNode( "element3" );
And you'll get the result you're looking for.
The following work perfect when i want to run xpath on the specified node.
XmlNodeList nodes = xmlDoc.SelectNodes(".//Child");
The "//" is a global look up.
What you'll need to do is get a list of all children
XmlNodeList nodes = xmlDoc.SelectNodes("//Child");
loop through that list and do a
XmlNode node = nodes.SelectSingleNode("element3");
This will return null if it's not there, and will step through every child looking.
the problem here is the XPath expression you are using, try it without the '//'. Like that:
node.SelectSingleNode( "element3" );
Read more here .

Categories

Resources