Xpath Queries and XmlNode manipulation in C#

Today I thought to write about some random examples of manipulating XML in C# using XPath.

Let's start with following sample contractors.xml file and let me show you how to quickly add, update & delete example!

<?xml version="1.0" encoding="utf-8"?>
<contractors>
<contractor title=".NET developer">
<name>Jay</name>
<email>abc@xyz.com</email>
<rate>25$</rate>
<country>India</country>
</contractor>
<contractor title="Windows 8 developer">
<name>Hrishikesh</name>
<email>def@xyz.com</email>
<rate>25$</rate>
<country>India</country>
</contractor>
<contractor title="Hadoop developer">
<name>Kapil</name>
<email>ghi@xyz.com</email>
<rate>35$</rate>
<country>India</country>
</contractor>
</contractors>

Listing 1.0 – Sample contractors.xml file

Add node
Following piece of code will add new node in our sample xml file.

//Add node
XmlDocument doc = new XmlDocument();
doc.Load("contractors.xml");
XmlNode currNode;

XmlDocumentFragment docFrag = doc.CreateDocumentFragment();

docFrag.InnerXml = @"<contractor title='.iOS developer'>
<name>Mahek</name>
<email>jkl@xyz.com</email>
<rate>45$</rate>
<country>USA</country>
</contractor>";

currNode = doc.DocumentElement;

currNode.InsertAfter(docFrag, currNode.LastChild);

doc.Save(contractorFileName);


Listing 1.1 – Adding node in contractors.xml

As per Listing 1.1, first we loaded xml file in XmlDocument to fire our XPath queries. Then we created document fragment and set it's InnerXml to new node value and finally we inserted it after current node.

Update node

Following piece of code will updated existing node for contractor Mahek.

//Update node
XmlDocument doc = new XmlDocument();
doc.Load(contractorFileName);

//Select the cd node with the matching title
XmlNode oldCd;
XmlElement root = doc.DocumentElement;

oldCd = root.SelectSingleNode("/contractors/contractor[@name='Mahek']");

XmlElement newCd = doc.CreateElement("contractor");

newCd.SetAttribute("title", "Android developer");

newCd.InnerXml = @"<name>Mahek</name>
<email>jkl@xyz.com</email>
<rate>45$</rate>
<country>USA</country>
";

root.ReplaceChild(newCd, oldCd);

doc.Save(contractorFileName);

Listing 1.2 – Updating node in contractors.xml


As you can see in Listing 1.2, first we select the node to be updated with SelectSingleNode method and we change it's InnerXml. Later we use ReplaceChild method to replace old node value with the new one.

Delete node
Following piece of code will delete existing node for contractor Kapil.

XmlDocument doc = new XmlDocument();

doc.Load(contractorFileName);

XmlNode cd;

XmlElement root = doc.DocumentElement;

cd = root.SelectSingleNode("/contractors/contractor[name='Kapil']");

root.RemoveChild(cd);

doc.Save(contractorFileName);

Listing 1.3 – Deleting node in contractors.xml

In Listing 1.3, the code is pretty much the same as update node but we use RemoveChild method to delete the node.

Let's see examples of querying XML for following few scenarios.

1. Query top 2 records only

XmlDocument doc = new XmlDocument();

doc.Load(contractorFileName);

XmlNodeList xnList = doc.SelectNodes("/contractors/contractor[position() <= 2]");

foreach (XmlNode xn in xnList)
{
Console.WriteLine(xn.InnerText);
}

Listing 1.4 – Querying xml using position function

2. Select record based on multiple criteria

XmlDocument doc = new XmlDocument();

doc.Load(contractorFileName);

XmlNodeList xnList = doc.SelectNodes("/contractors/contractor[@title='.NET developer' and @rate='25$']");

foreach (XmlNode xn in xnList)
{
Console.WriteLine(xn.InnerText);
}

Listing 1.5 – Querying xml involving multiple select criteria

3. Sort xml on name element as sort key

XmlDocument doc = new XmlDocument();

doc.Load(contractorFileName);

XPathNavigator navigator = doc.CreateNavigator();

XPathExpression selectExpression = navigator.Compile("/contractors/contractor");

selectExpression.AddSort("name", XmlSortOrder.Ascending, XmlCaseOrder.None, "", XmlDataType.Text );

XPathNodeIterator nodeIterator = navigator.Select(selectExpression);

while (nodeIterator.MoveNext())
{
XmlElement xnm = (XmlElement)((IHasXmlNode)nodeIterator.Current).GetNode();
Console.WriteLine(xnm["name"].InnerText);
}

Listing 1.6 – Sorting xml using XPathExpression

In Listing 1.6, we created XPathNavigator object and compiled our selectExpression. The XPathExpression has AddSort method where you can give attribute or element name to specify it as a sort key. Finally we iterate on XPathNodeIterator object and the output will show you name sorted alphabetically.

So far so good as we are playing with simple xml file. Let's have another sample drinks.xml as following:

<?xml version="1.0" encoding="utf-8" ?>
<drinks>
<drink>
<corona brand="Maxican">
<price>$3.50</price>
<alcohol>4%</alcohol>
</corona>
<kingfisher brand="Indian">
<price>$1.50</price>
<alcohol>8%</alcohol>
</kingfisher>
<knockout brand="Indian">
<price>$0.50</price>
<alcohol>6%</alcohol>
</knockout>
</drink>
</drinks>

Listing 1.7 – Sample drinks.xml file

As you see in Listing 1.7, there are different nodes under drink tag. So if you would like to obtain price for each drink, you would write something like following:

drinks/drink/corona/price
drinks/drink/kingfisher/price
drinks/drink/knockout/price

So you see the query differs for each node. But you can write a single query to obtain the same using // sequence.

drinks/drink//price

That way, our complete code would be as following:

XmlDocument doc = new XmlDocument();

doc.Load(drinksFileName);

XmlNodeList xnList = doc.SelectNodes("drinks/drink//price");

foreach (XmlNode xn in xnList)
{
Console.WriteLine(xn.InnerText);
}

Listing 1.8 – Select price of all drinks using // sequence

Amazon response xml
The other day I was playing with Amazon product advertising API. I came across scenario where I need to deal with namespace in xml document. Following is the portion of the XML that I was getting in response.

<?xml version="1.0"?>
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
<OperationRequest>
<RequestId>195e50a2-d4b2-4389-9ff5-66bdf8e87892</RequestId>
<Arguments>
<Argument Name="Operation" Value="ItemSearch"></Argument>
<Argument Name="Service" Value="AWSECommerceService"></Argument>
<Argument Name="AssociateTag" Value="mj"></Argument>
<Argument Name="BrowseNode" Value="962454"></Argument>
<Argument Name="Timestamp" Value="2013-11-02T15:39:14Z"></Argument>
<Argument Name="ResponseGroup" Value="ItemAttributes"></Argument>
<Argument Name="SearchIndex" Value="Music"></Argument>
</Arguments>
<RequestProcessingTime>0.1213320000000000</RequestProcessingTime>
</OperationRequest>
</ItemSearchResponse>

Listing 1.9 – Sample amazonresponse.xml file

So here we will select Name attribute value from all Argument tag. To do so, the code would go something like following:

XmlDocument doc = new XmlDocument();

XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);

mgr.AddNamespace("aws", "http://webservices.amazon.com/AWSECommerceService/2011-08-01");

doc.Load(amazonFileName);

XmlNodeList xnList = doc.SelectNodes("aws:ItemSearchResponse/aws:OperationRequest/aws:Arguments/aws:Argument/@Name", mgr);

foreach (XmlNode xn in xnList)
{
Console.WriteLine(xn.Value);
}

Listing 1.10 – Query using xml namespace manager

Here as per Listing 1.10, the XmlNamespaceManager object is designed to serve as a collection of namespace definitions. It is used by the .NET Framework XML processors to resolve and manage the scope of namespaces used in XML documents. Note that while accessing elements, we also need to precede each node with the namespace that we added in namespace manager.

Additional xpath query examples can be found on our XPath Examples for Node, Attribute, and Substring Queries page.

Source Code
You can grab the source code here.

By jay nanavati   Popularity  (5056 Views)