Relative XPath selection using XmlNode (c#) - c#

Say i have the following xml file:
<a>
<b>
<c></c>
</b>
<b>
<c></c>
</b>
</a>
var nodes = doc.SelectNodes("/a/b");
will select the two b nodes.
I then loop these two nodes suchas:
foreach (XmlNode node in nodes) { }
However, when i call node.SelectNodes("/a/b/c"); It still returns both values and not just the descendants.
Is it possible to select nodes only descening from the current node?

In the foreach loop, you already know that node is a /a/b in the original document - so to get just its c children simply use a relative xpath:
node.SelectNodes("c")

You can use node.SelectSingleNode("C");

/a/b[1]/c
For intance gets the nodelist of all the children of the first b that have the tagname c.
To get the first c as a singleton nodelist use /a/b[1]/c[1]. /a/b/c[1] again returns a nodelist of multiple nodes.
SelectSingleNode is probably misleading, as far as I know, XPath always returns a nodelist, which can contain optionally one node (or even be empty).
//c[1] just selects the first c in the document.

Related

C# Get all nodes in xml document but ignoring nested nodes

I have an xml document where i need to iterate over all nodes that are direct descendants of the parent.
For example i have the following xml document
<root>
<node1>val1</node1>
<node2>val2</node2>
<nodes>
<nestedNode>nestedvalue</nestedNode>
</nodes>
</root>
I have the following code which gets me all the elements:
XmlNodeList nodes = doc.SelectNodes("//*");
This returns node1, node2, and nestedNode. What i want is only node1 and node2 and to ignore any nested values.
Thanks in advance for any help.
To select elements that are children of the root element you would use the xpath:
/root/*
or in general:
/*/*
You should not traverse the all descendants here (//...) as that will go through all elements in the document. You would have to add additional filtering which would make the query unnecessarily complicated:
//*[parent::*[not(parent::*)]]
However, you want to filter out elements that do not have other child elements so you need to add the condition not(*):
/*/*[not(*)]

Iterate through XDocument when you dont know the structure

Is there any way to iterate through a XDocument when you dont know what the XML structure is (using c#)?
There is plenty of examples when you know the structure, like the answer to this question : C# - Select XML Descendants with Linq and C# Foreach XML Node
I've tried Descendants("A") where A is the example below - which in my foreach returns me one element with the name as the root and the value as 'all of the values concatinated into one string'
The reason I'm doing this is to anonymize certain nodes which I know the names.
The XDocument's I'm loading can be of any shape - so i've decided to just create a list which users can add to which contains these sensitive elements.
A solution I want to avoid is users creating XPath's for sensitive fields.
The XML is also sensitive so I cant share online literally but one example (out of 5) would look.
<A>
<B>
<C>
<D>
<dee>value1</dee>
<doo>value2</doo>
<date>value3</date>
<time>value4</time>
</D>
</C>
</B>
<E>
...ommited..this doc is 5000 lines long with 500~ unique node names
</E>
............
</A>
So is there a way to iterate without using Descendants?
Use .Descendants() to iterate every element.
xmlDoc.Root.Descendants()
.ToList()
.ForEach(e => Console.WriteLine(e.Name));
This is the way I went about it.
Descendants means you know the structure of the nodes before hand. Even with an empty method call to descendants (which should get everything from the root) wasn't giving me what I was expecting.
The below code should work for any XML document, without knowing the structure.
XmlDocument doc = new XmlDocument();
doc.Load(file);
using (XmlReader reader = new XmlNodeReader(doc))
{
while (reader.Read())
{
currentNodeName = reader.Name;

XML SelectNodes then SelectSingleNodes - only retrieves the first

I have the following XML
<root>
<Operations>
<OperationId>1</OperationId>
<OtherFields>...</OtherFields>
</Operations>
<Operations>
<OperationId>2</OperationId>
<OtherFields>...</OtherFields>
</Operations>
<Operations>
<OperationId>3</OperationId>
<OtherFields>...</OtherFields>
</Operations>
</root>
Using C# and System.XML namespace I get all operations using this code:
XmlNodeList operations= doc.SelectNodes("/root/Operations");
Now I need to loop through each operation and reference the fields (OperationId, OtherFields).
I try this:
foreach (XmlNode node in xnodes)
{
Console.WriteLine("Operation ID: {0}", node.SelectNodes("//OperationId")[0].InnerText);
}
However this just repeats the first OperationId - 1.
What is wrong?
Thanks,
Andrew
Your initial query selects all Operations nodes off the root like you're expecting. However your inner query in your loop does something different.
By starting your query with a //, you're querying relative to the root of the document. So you're actually selecting all OperationId nodes in the document, not just the descendant children of the current node. You then index the first node in that result for every iteration which is why you're seeing the first id repeating.
Since node refers to the current Operations node, to select the corresponding OperationId, your query should simply be:
OperationId
I should mention that since you're only trying to select the first element of the query, it would be better to use SelectSingleNode() instead of SelectNodes. If there are any nodes selected, the first will be returned.
node.SelectSingleNode("OperationId").InnerText
However since you're only trying to select an immediate child element, I wouldn't use an XPath query there, it's unnecessary. Just access the child element directly using the indexer.
var query = doc.SelectNodes("/root/Operations");
foreach (XmlNode node in query)
{
Console.WriteLine("Operation ID: {0}", node["OperationId"].InnerText);
}

Get specific data from XML document

I have xml document like this:
<level1>
<level2>
<level3>
<attribute1>...</attribute1>
<attribute2>false</attribute2>
<attribute3>...</attribute3>
</level3>
<level3>
<attribute1>...</attribute1>
<attribute2>true</attribute2>
<attribute3>...</attribute3>
</level3>
</level2>
<level2>
<level3>
<attribute1>...</attribute1>
<attribute2>false</attribute2>
...
...
...
I'm using c#, and I want to go thru all "level3", and for every "level3", i want to read attribute2, and if it says "true", i want to print the corresponding attribute3 (can be "level3" without these attributes).
I keep the xml in XmlDocument.
Then I keep all the "level3" nodes like this:
XmlNodeList xnList = document.SelectNodes(String.Format("/level1/level2/level3"));
(document is the XmlDocument).
But from now on, I don't know exactly how to continue. I tried going thru xnList with for..each, but nothing works fine for me..
How can I do it?
Thanks a lot
Well I'd use LINQ to XML:
var results = from level3 in doc.Descendants("level3")
where (bool) level3.Element("attribute2")
select level3.Element("attribute3").Value;
foreach (string result in results)
{
Console.WriteLine(result);
}
LINQ to XML makes all kinds of things much simpler than the XmlDocument API. Of course, the downside is that it requires .NET 3.5...
(By the way, naming elements attributeN is a bit confusing... one would expect attribute to refer to an actual XML attribute...)
You can use LINQ to XML and reading this is a good start.
You can use an XPath query. This will give you a XmlNodeList that contains all <attribute3> elements that match your requirement:
var list = document.SelectNodes("//level3[attribute2 = 'true']/attribute3");
foreach(XmlNode node in list)
{
Console.WriteLine(node.InnerText);
}
You can split the above xpath query in three parts:
"//level3" queries for all descendant elements named <level3>.
"[attribute2 = 'true']" filters the result from (1) and only keeps the elements where the child element <attribute2> contains the text true.
"/attribute3" takes the <attribute3> childnode of each element in the result of (2).

Read XMLDocument node without reading its child nodes in C#

Sample XML
<A>
<B>
<B1/>
<B2/>
<B3/>
<B4/>
<B5/>
</B>
<C>
<C1/>
<C2/>
<C3/>
<C4/>
<C5/>
</C>
</A>
Query:
C#
When I read the the child nodes of A it retuns nodes B & C with their child nodes.
Is there any possibility so that I can get only B & C without their respective child nodes
I need to populate the tree with this type of xml & the xml file is quite big. so I need to load the childs at the time of expanding any node
Requirement is
Suppose I try to expand A node the I want only B & C,
If I expand B then I want B1 to B5
Use a XmlReader. XmlDocument by design has to load the whole Xml document into memory.
if you use java, you can implement a SAX Handler building your DOM and ignoring the children.
It's a badly worded question so I'm not entirely sure what you are trying to do but if you just want all the child nodes of the root (A) then use an XmlDocument with XPath like this:
XmlDocument doc = new XmlDocument();
doc.Load(xmlFile);
XmlNodeList nodes = doc.SelectNodes("/A/*");
foreach(XmlNode node in nodes){
//DO STUFF
}
if i understand the question right, u need to get children of the node without getting their children. this can be done by xquery
(child::*)
so if u apply it in A node it will give B and C. if u apply it in B then it will give B1-B5.

Categories

Resources