Following is the example of an XML document.
<People>
<Person>
<Name>ABC </Name>
<SSN>111111</SSN>
<Address>asdfg</Address>
</Person>
</People>
I need to get the tag names but not the values between the tag names. That is, under the person tag, I should grab the Name, SSN, and Address nodes and not the ABC, 111111, and asdfg values.
I need to use LINQ to XML and query it in C#.
How can I do it?
This returns the names as a list of strings:
var doc = XDocument.Parse(#"<People>
<Person>
<Name>ABC </Name>
<SSN>111111</SSN>
<Address>asdfg</Address>
</Person>
</People>"
);
var list = doc.Root.Element("Person").Descendants()
.Select(node => node.Name.LocalName).ToList();
In case you're using an XElement instead of an XDocument, you can remove the .Root from the above code and get the correct results.
Create a class
public class Person
{
public string Name {get; set;}
public int SSN {get; set;}
public string Address {get; set;}
}
And create a new person this way;
List<Person> NewPersons = new List<Person>();
XDocument doc = XDocument.Load(xmlFile);
foreach(XElement xElem in doc.Descendants("Person"))
{
NewPersons.Add(new Person
{
Name = xElem. Element("Name").Value,
SSN = xElem.Element("SSN").Value,
Address = xElem.Element("Address").Value,
});
}
You can get it this way...
string xml = "<People> <Person> <Name>ABC </Name> <SSN>111111</SSN> <Address>asdfg</Address> </Person> <Person> <Name>ABC </Name> <SSN>111111</SSN> <Address>asdfg</Address> </Person> </People>";
XElement xmlData = XElement.Parse(xml);
foreach(XElement xmlPerson in xmlData.Elements("Person"))
{
List<string> TagsForThisPerson = new List<string>();
foreach(XElement xmlTag in xmlPerson.Elements())
{
TagsForThisPerson.Add(xmlTag.Name.ToString());
}
TagsForThisPerson.Dump();
}
Simple LINQ syntax to get the names is listed below. It assumes you have the XML loaded in a XDocument variable named doc.
var nodeNames = from node in doc.Descendants("Person").First().Descendants()
select node.Name.LocalName;
It only looks at the first person. If you have more than one in the XML document, the list of names is probably not what you would want (no reason to repeat all the names of the nodes over and over). This way, you get a list of just the node names for the first person, but it does assume that the first one would have a complete list of names. If they vary, you would need to build a distinct list from all the nodes.
Related
I am parsing an XML fragment using LINQ to XML and I am finding one of the nodes I am selecting is missing the child nodes I am expecting.
Example XML
<CustomerList>
<Customer>
<LastName>Smith</LastName>
<FirstName>Todd</FirstName>
</Customer>
<Customer>
<LastName>Jones</LastName>
<FirstName>Fred</FirstName>
</Customer>
<Customer>Tom Jones</Customer> <!-- Missing child nodes -->
</CustomerList>
When I try and pull out the LastName and FirstName values I get the error Object reference not set to an instance of an object.
Parsing XML with LINQ
XDocument xml = XDocument.Parse(xmlResponse);
List<CustomerModel> nodeList = xml.Descendants("CustomerList")
.Descendants("Customer")
.Select(x => new CustomerModel
{
LastName = x.Element("LastName").Value,
FirstName = x.Element("FirstName").Value,
}).ToList<CustomerModel>();
In the cases where the <Customer> nodes do not have the <LastName> and <FirstName> nodes how do I skip over them or better yet not even select them in the first place?
What I was suggesting is:
XDocument xml = XDocument.Parse(xmlResponse);
List<CustomerModel> nodeList = xml.Descendants("CustomerList")
.Descendants("Customer")
.Where(x => x.Element("LastName") != null && x.Element("FirstName") != null)
.Select(x => new CustomerModel
{
LastName = x.Element("LastName").Value,
FirstName = x.Element("FirstName").Value,
}).ToList<CustomerModel>();
I am trying to read an xml file (and later import the data in to a sql data base) which contains employees names address' etc.
The issue I am having is that in the xml the information for the address for the employee the node names are all the same.
<Employee>
<EmployeeDetails>
<Name>
<Ttl>Mr</Ttl>
<Fore>Baxter</Fore>
<Fore>Loki</Fore>
<Sur>Kelly</Sur>
</Name>
<Address>
<Line>Woof Road</Line>
<Line>Woof Lane</Line>
<Line>Woofington</Line>
<Line>London</Line>
</Address>
<BirthDate>1985-09-08</BirthDate>
<Gender>M</Gender>
<PassportNumber>123756rt</PassportNumber>
</EmployeeDetails>
</Employee>
I all other items are fine to extract and I have tried to use Linq to iterate through each "Line" node but it always just gives be the first Line and not the others.
var xAddreesLines = xEmployeeDetails.Descendants("Address").Select(x => new
{
address = (string)x.Element("Line").Value
});
foreach (var item in xAddreesLines)
{
Console.WriteLine(item.address);
}
I need to able to when I'm importing to my sql db that address line is separate variable
eg
var addressline1 = first <line> node
var addressline2 = second <line> node etc etc.
Any advice would be most welcome.
This should give you the expected output:-
var xAddreesLines = xdoc.Descendants("Address")
.Elements("Line")
.Select(x => new { address = (string)x });
You need to simply fetch the Line elements present inside Address node and you can project them. Also note there is no need to call the Value property on node when you use explicit conversion.
You can do it like this:
using System.Xml;
.
.
.
XmlDocument doc = new XmlDocument();
doc.Load("source.xml");
// if you have the xml in a string use doc.LoadXml(stringvar)
XmlNamespaceManager nsmngr = new XmlNamespaceManager(doc.NameTable);
XmlNodeList results = doc.DocumentElement.SelectNodes("child::Employee", nsmngr);
foreach (XmlNode result in results)
{
XmlNode namenode = result.SelectSingleNode("Address");
XmlNodeList types = result.SelectNodes("line");
foreach (XmlNode type in types)
{
Console.WriteLine(type.InnerText);
}
XmlNode fmtaddress = result.SelectSingleNode("formatted_address");
}
Refer to this question for the original source.
I am realy stuck on this one so all help is realy welcome. I have an XML document like this:
</root>
<person>
<name>John</name>
<lastname>Doe</lastname>
<laguage>NL</language>
<laguage>EN</language>
<laguage>DK</language>
</person>
<person>
<name>Samantha</name>
<lastname>Doe</lastname>
<laguage>NL</language>
<laguage>EN</language>
<laguage>DK</language>
</person>
</root>
So i want to read this in a place it in one of my Models so i do this like this here below.
XDocument xDoc = XDocument.Load("c:\\users\\acount\\documents\\visual studio 2010\\Projects\\XmlTest\\XmlTest\\XmlTest\\Sample.xml");
var data = from feed in xDoc.Descendants("person")
select new XmlModel
{
name = feed.Element("name").Value,
lastname = feed.Element("lastname").Value,
language = feed.Element("language").Value,
};
So like you would expect this only reads one language and not more than one. What i need i read all the languages but there is still a problem because some persons do know more languages than others. Can some one help me because i am getting sick and tired
languages = feed.Descendants("language").Select(x=>x.Value).ToList(),
var data = from feed in xDoc.Descendants("person")
select new XmlModel
{
name = feed.Element("name").Value,
lastname = feed.Element("lastname").Value,
language = feed.Elements("language").Select(x=>x.Value)
};
You need to make language of XmlModel as an IEnumerable<string> so that it can store multiple languages..
Let's say I have this XML file:
<Names>
<Name>
<FirstName>John</FirstName>
<LastName>Smith</LastName>
</Name>
<Name>
<FirstName>James</FirstName>
<LastName>White</LastName>
</Name>
</Names>
And now I want to print all the names of the node:
Names
Name
FirstName
LastName
I managed to get the all in a XmlNodeList, but I dont know how SelectNodes works.
XmlNodeList xnList = xml.SelectNodes(/*What goes here*/);
I want to select all nodes, and then do a foreach of xnList (Using the .Value property I assume).
Is this the correct approach? How can I use the selectNodes to select all the nodes?
Ensuring you have LINQ and LINQ to XML in scope:
using System.Linq;
using System.Xml.Linq;
If you load them into an XDocument:
var doc = XDocument.Parse(xml); // if from string
var doc = XDocument.Load(xmlFile); // if from file
You can do something like:
doc.Descendants().Select(n => n.Name).Distinct()
This will give you a collection of all distinct XNames of elements in the document. If you don't care about XML namespaces, you can change that to:
doc.Descendants().Select(n => n.Name.LocalName).Distinct()
which will give you a collection of all distinct element names as strings.
There are several ways of doing it.
With XDocument and LINQ-XML
foreach(var name in doc.Root.DescendantNodes().OfType<XElement>().Select(x => x.Name).Distinct())
{
Console.WriteLine(name);
}
If you are using C# 3.0 or above, you can do this
var data = XElement.Load("c:/test.xml"); // change this to reflect location of your xml file
var allElementNames =
(from e in in data.Descendants()
select e.Name).Distinct();
Add
using System.Xml.Linq;
Then you can do
var element = XElement.Parse({Your xml string});
Console.Write(element.Descendants("Name").Select(el => string.Format("{0} {1}", el.Element("FirstName").Value, el.Element("LastName").Value)));
I'm having trouble trying to update my xml file with a new value. I have a class Person, which only contains 2 strings, name and description. I populate this list and write it as an XML file. Then I populate a new list, which contains many of the same names, but some of them contains descriptions that the other list did not contain. How can I check if the name in the current XML file contains a value other than "no description", which is the default for "nothing"?
Part of the xml file:
<?xml version="1.0" encoding="utf-8"?>
<Names>
<Person ID="2">
<Name>Aaron</Name>
<Description>No description</Description>
</Person>
<Person ID="2">
<Name>Abdi</Name>
<Description>No description</Description>
</Person>
</Names>
And this is the method for writing the list to the xml file:
public static void SaveAllNames(List<Person> names)
{
XDocument data = XDocument.Load(#"xml\boys\Names.xml");
foreach (Person person in names)
{
XElement newPerson = new XElement("Person",
new XElement("Name", person.Name),
new XElement("Description", person.Description)
);
newPerson.SetAttributeValue("ID", GetNextAvailableID());
data.Element("Names").Add(newPerson);
}
data.Save(#"xml\boys\Names.xml");
}
In the foreach loop how do I check if the person's name is already there, and then check if the description is something other than "no description", and if it is, update it with the new information?
I'm not sure I understand properly what you want, but I'm assuming you want to update the description only when the name is already there and the description is currently No description (which you should probably change to an empty string, BTW).
You could put all the Persons into a Dictionary based by name:
var doc = …;
var persons = doc.Root.Elements()
.ToDictionary(x => (string)x.Element("Name"), x => x);
and then query it:
if (persons.ContainsKey(name))
{
var description = persons[name].Element("Description");
if (description.Value == "No description")
description.Value = newDescription;
}
That is, if you care about performance. If you don't, you don't need the dictionary:
var person = doc.Root.Elements("Person")
.SingleOrDefault(x => (string)x.Element("Name") == name);
if (person != null)
{
var description = person.Element("Description");
if (description.Value == "No description")
description.Value = newDescription;
}
You can use the Nodes-Method on XElement and check manually.
But i will advise you to use the XPathEvaluate-Extension Method
For XPath expression take a look at:
How to check if an element exists in the xml using xpath?
I think you could create a peoplelist which only contains people not in the xml.
like ↓
var containlist = (from p in data.Descendants("Name") select p.Value).ToList();
var result = (from p in peoplelist where !containlist.Contains(p.Name) select p).ToList();
so that , you would no need to change anything with your exist method ...
just call it after..
SaveAllNames(result);