Retrieve nodes from XML belonging to a specific node - c#

I have a XML like this:
<ITEM>
<RACES>
<TAB>
<NUMBER>1</NUMBER>
<A></A>
<B></B>
</TAB>
<TAB>
<NUMBER>2</NUMBER>
<A></A>
<B></B>
</TAB>
</RACES>
</ITEM>
is it possible to retrieve as XmlNodeList all the As and Bs nodes that belong to only TAB with NUMBER 1?
I use the following codes, but it gives me of course 2 nodes. I want only 1 node :
XmlNodeList xnList = xml.SelectNodes("/ITEM/RACES/TAB/A");

You can do xmlDocument.SelectNodes(expression)
where if you need both nodes A & B
expression = #"//TAB[NUMBER=1]/A|//TAB[NUMBER=1]/B"
if you need only A node seperately
expression = #"//TAB[NUMBER=1]/A"
if you need only B node seperately
expression = #"//TAB[NUMBER=1]/B"

Try as below:
XmlNodeList xnList = xml.SelectNodes("/RACES/TAB");
foreach (XmlNode xn in xnList)
{
int num = xn["NUMBER"].InnerText;
if(num==1)
{
Console.WriteLine("Nodes: {0} {1}", xn["A"], xn["B"]);
}
}

I strongly recommend Linq to Xml. You can knock it out in one statement:
var nodes_A_and_B = XDocument.Parse(xml)
.Descendants("TAB")
.Where(t => t.Element("NUMBER").Value == "1")
.Select(t => new
{
A = t.Element("A"),
B = t.Element("B")
});

it will be return what you need
Happy coding
var items = XElement.Parse(xmlelemet)
.Elements("RACES")
.Elements("TAB")
.Where(n => n.Attribute("NUMBER").Value == 1)
.Elements();

Related

Fetching XML child values in c#

I have some data i need to fetch from a xml file.
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
How would I fetch the year based of the category?
So it would be like
writeline( FetchYearFromCategory("cooking") );
and it would output 2005
Hope you can understand
You can use LINQ to Xml
// Load xml in the memory
var document = XDocument.Load(pathToXmlFile);
// Filter xml to find required data
var yearsOfCooking =
document.Descendants("book")
.Where(book => book.Attribute("category").Value == "cooking")
.Select(book => book.Element("year").Value);
// Print result
foreach (var year in yersOfCooking)
{
Console.WriteLine(year);
}
The tool you are looking for is called XPath. You can define which elements are going to match using a relatively easy syntax. W3Schools has a pretty good tutorial, if you are not already familiar with it. Here is the link.
The syntax to use it would be:
// This will fetch all "book" items in the root node
// that have the category = "cooking"
// and select their year node.
var strXPathExpression = "/book[#category='cooking']/year";
var doc = new XmlDocument();
doc.Load("YourXmlFile.xml");
var root = doc.DocumentElement;
var desiredNodes = root.SelectNodes(strXPathExpression).
foreach (var node in desiredNodes)
{
// You have your year here
var year = node.Value;
}
You can use System.Xml to access XML Files in C#.NET
public int FetchYearFromCategory(string category){
XmlDocument doc = new XmlDocument();
doc.Load("path_to_your_file");
XmlNodeList allBooks = doc.GetElementsByTagName("book");
foreach (XmlNode singleBookNode in allBooks)
{
if(singleBookNode.Attributes["category"] != null && singleBookNode.Attributes["category"].Value.Equals(category))
{
XmlNodeList childNodes = singleBookNode.ChildNodes;
foreach (XmlNode childNode in childNodes)
{
if(childNode.Name.Equals("year"))
{
return Int32.Parse(childNode.InnerText);
}
}
}
}
return -1;}
I think the above should do you the job.

use LINQ on XmlNodeList

<X version="1.0">
<Y id="abc" abv="a"/>
<Y id="edf" abv="e"/>
</X>
I want to select the node whose id is "abc", and return its abv "a".
XmlDocument doc = new XmlDocument();
doc.Load(filePath);
XmlNodeList list = doc.SelectNodes("X/Y");
var node = list.Cast<XmlNode>().Where(node => node["id"].InnerText == "abc")
.Select(x=>x["abv"].InnerText);
But it does't work, node["id"].InnerText is always "". Can you point out where is a problem?
Thanks a lot
Aside from the fact what your code snippet wouldn't be compiled because of non-unique node variable (first outside of linq query and second in "where" method lambda), you have also missed Attributes in your query.
It should be something like
var node = list.Cast<XmlNode>()
.Where(n => n.Attributes["id"].InnerText == "abc")
.Select(x => x.Attributes["abv"].InnerText);
The InnerText for a node is the text that appears between <node> and </node>. So for, eg <Y attributes /> there is no inner text.
You need to use node => node.Attributes["id"].Value == "abc"
Just cast XmlNodeList to List, like that:
List<XmlNode> list = new List<XmlNode>();
foreach(XmlNode a in xmlNodeList)
{
list.Add(a);
}
list.OrderBy((element) => element.ChildNodes[0].InnerText);

Select Parent XML(Entire Hierarchy) Elements based on Child element values LINQ

I have the following XML and query through the ID,how do get the Parent Hierarchy
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>
In this if i query(Id = 4) and find out the Parent elements using Linq in the particular element how to get the following output with Hierarchy.
<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4"/>
</Child3>
</Child2>
</Child1>
</Child>
Thanks In Advance.
Assume you want just one node parent tree:
string xml = #"<Child>
<Child1 Id="1">
<Child2 Id="2">
<Child3 Id="3">
<Child4 Id="4">
<Child5 Id="5"/>
<Child6 Id="6"/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
TextReader tr = new StringReader(xml);
XDocument doc = XDocument.Load(tr);
IEnumerable<XElement> myList =
from el in doc.Descendants()
where (string)el.Attribute("Id") == "4" // here whatever you want
select el;
// select your hero element in some way
XElement hero = myList.FirstOrDefault();
foreach (XElement ancestor in hero.Ancestors())
{
Console.WriteLine(ancestor.Name); // rebuild your tree in a separate document, I print ;)
}
To search for every element of your tree iterate retrieve the node with the select query without the where clause and call the foreach for every element.
Based on the sample XML provided, you could walk up the tree to find the parent node once you've found the node in question:
string xml =
#"<Child>
<Child1 Id='1'>
<Child2 Id='2'>
<Child3 Id='3'>
<Child4 Id='4'>
<Child5 Id='5'/>
<Child6 Id='6'/>
</Child4>
</Child3>
</Child2>
</Child1>
</Child>";
var doc = XDocument.Parse( xml );
// assumes there will always be an Id attribute for each node
// and there will be an Id with a value of 4
// otherwise an exception will be thrown.
XElement el = doc.Root.Descendants().First( x => x.Attribute( "Id" ).Value == "4" );
// discared all child nodes
el.RemoveNodes();
// walk up the tree to find the parent; when the
// parent is null, then the current node is the
// top most parent.
while( true )
{
if( el.Parent == null )
{
break;
}
el = el.Parent;
}
In Linq to XML there is a method called AncestorsAndSelf on XElement that
Returns a collection of elements that contain this element, and the
ancestors of this element.
But it will not transform your XML tree the way you want it.
What you want is:
For a given element, find the parent
Remove all elements from parent but the given element
Remove all elements from the given element
Something like this in Linq (no error handling):
XDocument doc = XDocument.Parse("<xml content>");
//finding element having 4 as ID for example
XElement el = doc.Descendants().First(el => el.Attribute("Id").Value == "4");
el.RemoveNodes();
XElement parent = el.Parent;
parent.RemoveNodes();
parent.Add(el);
[Edit]
doc.ToString() must give you what you want as a string.
[Edit]
Using RemoveNodes instead of RemoveAll, the last one also removes attributes.
Removing nodes from the chosen element too.
I found the following way
XElement elementNode = element.Descendants()
.FirstOrDefault(id => id.Attribute("id").Value == "4");
elementNode.RemoveNodes();
while (elementNode.Parent != null)
{
XElement lastNode = new XElement(elementNode);
elementNode = elementNode.Parent;
elementNode.RemoveNodes();
elementNode.DescendantsAndSelf().Last().AddFirst(lastNode);
}
return or Print elementNode.

xpath to select elements with last child value

This is my xml file
<profiles>
<profile id='8404'>
<name>john</name>
<name>mark</name>
</profile>
<profile id='8405'>
<name>john</name>
</profile>
</profiles>
and I want to select profiles where last "name" child value= john, the result should contains the profile with id=8405 only
what it the xpath that can evaluate this?
here is my trial:
var filterdFile = profilefileXML.XPathSelectElements("/profiles/profile[name[last()]='john']");
but it doesn't make sense.
Updated:
My trail is correct, there was only a syntax error in it. Thanks all
You can apply multiple indexing operations with successive [...]:
var doc = XDocument.Parse(xml); //the xml from your question
var node = doc.XPathSelectElement("/profiles/profile[name='john'][last()]");
Console.WriteLine(node.Attribute("id").Value); //outputs 8405
This will return the last profile element that contains the element name with a value of john.
If you on the other hand want to return all elements which last name element has a value of john, your XPath should work already:
var nodes = doc.XPathSelectElements("/profiles/profile[name[last()]='john']");
foreach (var node in nodes)
{
Console.WriteLine(node.Attribute("id").Value);
}
You can also try LINQ
XDocument xDoc = XDocument.Load("data.xml");
var matches = xDoc.Descendants("profile")
.Where(profile => XElement.Parse(profile.LastNode.ToString()).Value == "john");
And you can access the xml data with a foreach
foreach(XElement xEle in lastEle)
{
var xAttribute = xEle.Attribute("id");
if (xAttribute != null)
{
var id = xAttribute.Value;
}
var lastName = XElement.Parse(xEle.LastNode.ToString()).Value;
}

Parsing XmlDocument with Variable Parent Tags

Assuming I have an XmlDocument like this:
<xmlFile>
<details>
<code1>ADJ</code1>
<code2>ADC </code2>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
</details>
<details>
<code1>ADJ</code1>
<code2>SCC </code2>
<Shipment>
<foo></foo>
<bar></bar>
</Shipment>
</details>
</xmlFile>
I need to process each in an xml file but only shipments that fall under the tags with a child node with a value of "ADC". So far I have:
// Assume there is an XmlDocument named xml
XmlNodeList details= xml.GetElementsByTagName("details");
foreach (XmlNode node in details)
{
if (node["code2"].InnerText == "ADC ")
{
// Get each shipment and process it accordingly.
}
}
I can't figure out what to do next. Thanks.
Assuming Data\Sample.xml contains xml as mentioned in the question,
Following is the XLINQ query
XElement root = XElement.Parse(File.ReadAllText(#"Data\Sample.xml"));
var adcShipment = root.Descendants().Where(e=>String.Equals(e.Value, "ADC "));
//next query for nodes/elements inside/next to ADC shipments
XPath can simplify your search for matches:
foreach (XmlNode node in xml.SelectNodes("/xmlFile/details[normalize-space(code2)='ADC']"))
{
string foo = node.SelectSingleNode("foo").InnerText;
string bar = node.SelectSingleNode("bar").InnerText;
}
I'm in the process of adding XPath parsing to this library: https://github.com/ChuckSavage/XmlLib/
I modified it so you can do this:
XElement root = XElement.Load(file);
var shipments = root.XPath("details[starts-with(*,'ADC')]/Shipment");
Long-hand that looks like:
var shipments = root.Elements("details")
.Where(x => x.Elements().Any(xx => ((string)xx).StartsWith("ADC")))
.Elements("Shipment");
This is the sort of thing you're after
XmlNodeList details = xml.GetElementsByTagName("details");
foreach (XmlNode node in details)
{
if (node["code2"].InnerText.Trim() == "ADC")
{
// Get each shipment and process it accordingly.
foreach(XmlNode shipment in node.SelectNodes("Shipment"))
{
var foo = shipment.SelectSingleNode("foo");
var bar = shipment.SelectSingleNode("bar");
}
}
}

Categories

Resources