C# XML Read All Child Nodes Of A Specific Node - c#

So I have a program that reads all the name nodes in a XML file and adds these to a Combo Box. On a button click, it then takes this response and needs to get all the other data from the child nodes of the node the name is in.
The XML document:
<People>
<Person>
<Name>Greg</Name>
<Age>23</Age>
<Height>200</Height>
</Person>
<Person>
<Name>John</Name>
<Age>34</Age>
<Height>230</Height>
</Person>
</People>
What I've got so far:
XmlDocument Doc = new XmlDocument();
Doc.Load(FilePath);
foreach (XmlNode Node in Doc.SelectNodes("People/Person"))
{
comboBox1.Items.Add(Node.SelectSingleNode("Name").InnerText);
}
string RegPicked = comboBox1.SelectedItem.ToString();
foreach (XmlNode xNode in Doc.SelectNodes("People/Person"))
if (xNode.SelectSingleNode("Name").InnerText == RegPicked)
{
textBox1.Text = xNode.ParentNode.ChildNodes.ToString();
}
Doc.Save(FilePath);
When I run the code I just get "System.Xml.XmlChildNodes" in the text box.
I know I've done something wrong but I'm not sure what.

you have to distinguished child node:
textBox1.Text = xNode.ParentNode.ChildNodes.SelectSingleNode("age").InnerText;

Do this, you will get all the elements inside the parent of the node you are searching the value of.
string str = #"<People>
<Person>
<Name>Greg</Name>
<Age>23</Age>
<Height>200</Height>
</Person>
<Person>
<Name>John</Name>
<Age>34</Age>
<Height>230</Height>
</Person>
</People>";
XDocument xdoc = XDocument.Parse(str);
var xmlURL = (from el in xdoc.Descendants("Name")
where el.Value == "John"
select el.Parent).First().ToString();

Related

How to store inner text of nodes with same names to a List?

Using C#, I need to store all the data of the inner text of Email nodes into a list, and all the data of the inner text of Related nodes into a list for each person separately. For now I was only able to store only first "email" node and first "related" node into lists. I get this:
,when I should get this
.
How to get the right answer? This is my xml file:
<? xml version="1.0" encoding="utf-8"?>
<People>
<Person>
<Name>Toni</Name>
<Email>a#g.c</Email>
<Email>b#g.c</Email>
<Email>c#g.c</Email>
<Related>Friend1</Related>
<Related>Friend2</Related>
</Osoba>
<Osoba>
<Name>Deni</Name>
<Email>d#g.c</Email>
<Email>e#g.c</Email>
<Email>f#g.c</Email>
<Related>Friend3</Related>
<Related>Friend4</Related>
</Osoba>
</People>
I am assuming the following xml Instead of the one you posted since it is wrong.
<?xml version="1.0" encoding="utf-8"?>
<People>
<Person>
<Name>Toni</Name>
<Email>a#g.c</Email>
<Email>b#g.c</Email>
<Email>c#g.c</Email>
<Related>Friend1</Related>
<Related>Friend2</Related>
</Person>
<Person>
<Name>Deni</Name>
<Email>d#g.c</Email>
<Email>e#g.c</Email>
<Email>f#g.c</Email>
<Related>Friend3</Related>
<Related>Friend4</Related>
</Person>
</People>
I am using LINQ to XML.(Another way is to use XmlDocument)
String path = "Path of your xml file";
XDocument doc = XDocument.Load(path);
var nodes = doc.Descendants("Person");
foreach (XElement node in nodes)
{
var name = node.Element("Name").Value;
var emails = node.Elements("Email").Select(x => x.Value);
}

Is this correct way to Parse inner element of XML by LINQ in C#

Always confused with LINQ to XML. I would like to extract emails as IEnumerable from the following XML.
String xml =#"<?xml version=""1.0"" encoding=""utf-8""?>
<people>
<person firstname=""John"" lastname=""doe"">
<contactdetails>
<emailaddress>john#unknown.com</emailaddress>
</contactdetails>
</person>
<person firstname=""Jane"" lastname=""doe"">
<contactdetails>
<emailaddress>jane#unknown.com</emailaddress>
<phonenumber>001122334455</phonenumber>
</contactdetails>
</person>
</people>";
After some trials I found the following code works, but I don't like to use ElementAt(0).Value (normally I don't see in other example code). What would be the best way to use LINQ here?
XDocument doc = XDocument.Parse(xml);
var emails = from p in doc.Descendants("person")
where p.Descendants("emailaddress").Any()
let email = (string)p.Descendants("emailaddress").ElementAt(0).Value
select email;
get all emails from xml
var e_mailsAll = doc.Descendants("person")
.Descendants("emailaddress")
.Select(x=>x.Value);
get first email of each person, if someone has more than 1
var e_mailsFirst = doc.Descendants("person")
.Select(x=>x.Descendants("emailaddress").FirstOrDefault())
.Where(x=>x!=null)
.Select(x=>x.Value);
You can use XPathSelectElements in order to extract IEnumerable<XElement> using XPath:
string xml = #"<?xml version=""1.0"" encoding=""utf-8""?>
<people>
<person firstname=""John"" lastname=""doe"">
<contactdetails>
<emailaddress>john#unknown.com</emailaddress>
</contactdetails>
</person>
<person firstname=""Jane"" lastname=""doe"">
<contactdetails>
<emailaddress>jane#unknown.com</emailaddress>
<phonenumber>001122334455</phonenumber>
</contactdetails>
</person>
</people>";
IEnumerable<XElement> elements = XDocument
.Parse(xml)
.XPathSelectElements("people/person/contactdetails/emailaddress");
Then, you will be able to use LINQ Select in order to extract an array of emails:
string[] elements = XDocument
.Parse(xml)
.XPathSelectElements("people/person/contactdetails/emailaddress")
.Select(x => x.Value)
.ToArray();

validating an xml and finding line and column of error

i have an xml document of this kind
<xml>
<person name="a">
<age>21</age>
<salary>50000></salary>
</person>
<person name="b">
<age>25</age>
<salary>30000></salary>
</person>
<person name="c">
<age>30</age>
<salary>60000></salary>
</person>
<person name="d">
<age>35</age>
<salary>150000></salary>
</person>
</xml>
Now im trying to validate this document by passing this to a method like this
validate(string file)
{
// here i have some logic
// say i am trying to check if the salary is >50000 and age > 30
// if it doesn't satisfy the condition i have to return an error
}
I am able to achieve this . What i really want is to know where exactly the error in xml document is , like which line and column.
How should i do this ? any suggestions?
You are closing your 'age' tag like this
<age>35<age/>
It should be like
<age>35</age>
hope this works for you.
Firstly, you have not specified how you are parsing your XML in c#. It matters.
Now as for validity of your XML document, a valid xml document should have these:
XML documents must have a root element
XML elements must have a closing tag
XML tags are case sensitive
XML elements must be properly nested
-XML attribute values must be quoted
now try adding this to the top of your xml doc
<?xml version="1.0" encoding="ISO-8859-1"?>
Now, to parse XML document, you can use either XmlDocument class or Linq's XDocument class.
Lets take example of XmlDocument.
if you have a xml string, load Xml as below:
XmlDocument doc = new XmlDocument();
doc.LoadXml(stringXML);
foreach(XmlNode node in doc.SelectNodes("xml/person/salary"))
{
var strSalary = node.InnerText;
var intSalary = Convert.ToInt32(strSalary??0);
}
if you have a xml file, load Xml as below:
XmlDocument doc = new XmlDocument();
doc.Load(XMLFilePath);
Full example:
string xml = #"<xml>
<person name=""a"">
<age>21</age>
<salary>50000</salary>
</person>
<person name=""b"">
<age>25</age>
<salary>30000</salary>
</person>
<person name=""c"">
<age>30</age>
<salary>60000</salary>
</person>
<person name=""d"">
<age>35</age>
<salary>150000</salary>
</person>
</xml>";
using (var sr = new StringReader(xml))
{
var xml2 = XDocument.Load(sr, LoadOptions.SetLineInfo);
foreach (var person in xml2.Root.Elements())
{
//string name = (string)person.Attribute("name"); // Unused
int age = (int)person.Element("age");
int salary = (int)person.Element("salary");
// Your check
bool error = salary > 50000 && age > 30;
if (error)
{
// IMPORTANT PART HERE!!!
int lineNumber = -1;
int colNumber = -1;
var lineInfo = (IXmlLineInfo)person;
if (lineInfo.HasLineInfo())
{
lineNumber = lineInfo.LineNumber;
colNumber = lineInfo.LinePosition;
}
return string.Format("Error on line {0}, col {1}", lineNumber, colNumber);
// END IMPORTANT PART!!!
}
}
}
The "trick" is that XElement implement IXmlLineInfo (as explained here), but you have to load the document with LoadOptions.SetLineInfo.

How to get specific element Count in XML or XElement variable

Consider this XML:
<Employees>
<Person>
<ID>1000</ID>
<Name>Nima</Name>
<LName>Agha</LName>
</Person>
<Person>
<ID>1001</ID>
<Name>Ligha</Name>
<LName>Ligha</LName>
</Person>
<Person>
<ID>1002</ID>
<Name>Jigha</Name>
<LName>Jigha</LName>
</Person>
<Person>
<ID>1003</ID>
<Name>Aba</Name>
<LName>Aba</LName>
</Person>
</Employees>
I declare a XElement variable and create the XML assigning it to that. How I can get count of ID elements in this XML variable in C#?
Prerequisite: in order to use .Count() you need to import the namespace System.Linq:
using System.Linq;
You can filter the descendant elements using the Descendants method with the name "ID", then count the results:
int count = xml.Descendants("ID").Count();
Be aware that Descendants looks through all levels. If you had an element other than Person that also had an ID child element, you would want to be more specific. In that case, to count ID child elements that belong to Person elements, you would use:
int count = xml.Elements("Person")
.Elements("ID")
.Count();
XmlDocument xmldoc = new XmlDocument();
xmldoc.Load(XmlPath);
var totalItems = xmldoc.SelectNodes(
"/root/node/LastName/").Count;
var cnt = element.Descendants("ID").Count();

Parsing Optional Tags in C#

I'm trying to find an easy and slick way to do the following requirement.
I have a XML message with this arrangement:
<persons>
<person>
<firstName>Mike</firstName>
<middleName>K.</middleName>
<lastName>Kelly</lastName>
</person>
<person>
<firstName>Steve</firstName>
<lastName>David</lastName>
</person>
<person>
<firstName>Laura</firstName>
<middleName>X.</middleName>
<lastName>Xavier</lastName>
</person>
</persons>
I want to parse this XML using xPath expressions.
persons/person/firstName
persons/person/middleName
persons/person/lastName
My objective is store firstName, middleName and lastName tag values like this into a list of string objects like this:
firstNameList[0] = "Mike";
firstNameList[1] = "Steve";
firstNameList[2] = "Laura";
middleNameList[0] = "K.";
middleNameList[1] = null;
middleNameList[2] = "X.";
lastNameList[0] = "Kelly";
lastNameList[1] = "David";
lastNameList[2] = "Xavier";
In my C# code, I do this:
XmlNodeList firstNameNodeList = xmlDoc.SelectNodes("persons/person/firstName", nsmgr);
XmlNodeList middleNameNodeList = xmlDoc.SelectNodes("persons/person/middleName", nsmgr);
XmlNodeList lastNameNodeList = xmlDoc.SelectNodes("persons/person/lastName", nsmgr);
The problem with this code is that for middle name, I don't have it for 2nd person in my XML list. So the middleNameNodeList returns 2 values (K. and X.) but I wouldn't know whether the 1st or 2nd or 3rd person's middle name is missing.
I was hoping that SelectNodes() API would provide an iteration or index ID as which repeating element has a given value.
Please suggest me an easiest way to achieve what I needed? Thanks so much for your help, JK
How about this?
foreach (Node person in xmlDoc.SelectNodes("persons/person", nsmgr))
{
firstNameNodeList.Add(person.SelectSingleNode("firstName", nsmgr));
middleNameNodeList.Add(person.SelectSingleNode("middleName", nsmgr));
lastNameNodeList.Add(person.SelectSingleNode("lastName", nsmgr));
}
Intead of getting a list of names, try getting a list of persons, then iterate the list and get their names.
You just have to iterate over persons/person and handle each individually - this would work:
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(#"test.xml");
var persons = xmlDoc.SelectNodes("persons/person");
foreach (XmlNode person in persons)
{
string firstName = person.SelectSingleNode("firstName").InnerText;
string middleName = (person.SelectSingleNode("middleName") != null)
? person.SelectSingleNode("middleName").InnerText
: null;
string lastName = person.SelectSingleNode("lastName").InnerText;
}
Try
<persons>
<person>
<firstName>Mike</firstName>
<middleName>K.</middleName>
<lastName>Kelly</lastName>
</person>
<person>
<firstName>Steve</firstName>
<middleName />
<lastName>David</lastName>
</person>
<person>
<firstName>Laura</firstName>
<middleName>X.</middleName>
<lastName>Xavier</lastName>
</person>
</persons>
<person>
<firstName>Steve</firstName>
<middleName />
<lastName>David</lastName>
</person>
this should return "K","","X" for InnnerText

Categories