Parse XML with LINQ to get children

<?xml version="1.0" standalone="yes"?> <CompanyInfo> <Employee name="Jon" deptId="123"> <Region name="West"> <Area code="96" /> </Region> <Region name="East"> <Area code="88" /> </Region> </Employee> </CompanyInfo> public class Employee { public string EmployeeName { get; set; } public string DeptId { get; set; } public List<string> RegionList {get; set;} } public class Region { public string RegionName { get; set; } public string AreaCode { get; set; } } 

I am trying to read this XML data while I tried this:

 XDocument xml = XDocument.Load(@"C:\data.xml"); var xElement = xml.Element("CompanyInfo"); if (xElement != null) foreach (var child in xElement.Elements()) { Console.WriteLine(child.Name); foreach (var item in child.Attributes()) { Console.WriteLine(item.Name + ": " + item.Value); } foreach (var childElement in child.Elements()) { Console.WriteLine("--->" + childElement.Name); foreach (var ds in childElement.Attributes()) { Console.WriteLine(ds.Name + ": " + ds.Value); } foreach (var element in childElement.Elements()) { Console.WriteLine("------->" + element.Name); foreach (var ds in element.Attributes()) { Console.WriteLine(ds.Name + ": " + ds.Value); } } } } 

This allows me to get each node, its name and attribute value, and therefore I can save this data in the corresponding field in the database, but it seems long, is not flexible, for example, if the XML structure changes all the ones that need to be performed for foreach , it’s also difficult to filter the data in this way, I need to write certain if statements to filter the data (for example, get employees only from the west, etc.)

I was looking for a more flexible way using linq, something like this:

 List<Employees> employees = (from employee in xml.Descendants("CompanyInfo") select new employee { EmployeeName = employee.Element("employee").Value, EmployeeDeptId = ?? get data, RegionName = ?? get data, AreaCode = ?? get data,, }).ToList<Employee>(); 

But I'm not sure how I can get values ​​from child nodes and apply filtering (to get only certain employees). Is it possible? Any help is appreciated.

thanks

+6
source share
3 answers
 var employees = (from e in xml.Root.Elements("Employee") let r = e.Element("Region") where (string)r.Attribute("name") == "West" select new Employee { EmployeeName = (string)e.Attribute("employee"), EmployeeDeptId = (string)e.Attribute("deptId"), RegionName = (string)r.Attribute("name"), AreaCode = (string)r.Element("Area").Attribute("code"), }).ToList(); 

However, if you change the structure of the XML file, it will still require a request revision.

Edit

Request for several regions per employee:

 var employees = (from e in xml.Root.Elements("Employee") select new Employee { EmployeeName = (string)e.Attribute("employee"), DeptId = (string)e.Attribute("deptId"), RegionList = e.Elements("Region") .Select(r => new Region { RegionName = (string)r.Attribute("name"), AreaCode = (string)r.Element("Area").Attribute("code") }).ToList() }).ToList(); 

Then you can filter the list only for employees from this area:

 var westEmployees = employees.Where(x => x.RegionList.Any(r => r.RegionName == "West")).ToList(); 
+8
source

You can track the structure:

 from employee in xml .Element("CompanyInfo") // must be root .Elements("Employee") // only directly children of CompanyInfo 

or less strictly

 from employee in xml.Descendants("Employee") // all employees at any level 

And then get the information you need:

  select new Employee { EmployeeName = employee.Attribute("name").Value, EmployeeDeptId = employee.Attribute("deptId").Value, RegionName = employee.Element("Region").Attribute("name").Value, AreaCode = employee.Element("Region").Element("Area").Attribute("code").Value, } 

And with additional information about several regions, assuming the List<Region> Regions property:

  select new Employee { EmployeeName = employee.Attribute("name").Value, EmployeeDeptId = employee.Attribute("deptId").Value, //RegionName = employee.Element("Region").Attribute("name").Value, //AreaCode = employee.Element("Region").Element("Area").Attribute("code").Value, Regions = (from r in employee.Elements("Region") select new Region { Name = r.Attribute("name").Value, Code = r.Element("Area").Attribute("code").Value, }).ToList(); } 
+6
source

You can make a choice in one request, and then on the second, or combine them as a single request:

Two queries:

  // do te transformation var employees = from employee in xml.Descendants("CompanyInfo").Elements("Employee") select new { EmployeeName = employee.Attribute("name").Value, EmployeeDeptId = employee.Attribute("deptId").Value, Regions = from region in employee.Elements("Region") select new { Name = region.Attribute("name").Value, AreaCode = region.Element("Area").Attribute("code").Value, } }; // now do the filtering var filteredEmployees = from employee in employees from region in employee.Regions where region.AreaCode == "96" select employee; 

Combined single request (same output):

  var employees2 = from selectedEmployee2 in from employee in xml.Descendants("CompanyInfo").Elements("Employee") select new { EmployeeName = employee.Attribute("name").Value, EmployeeDeptId = employee.Attribute("deptId").Value, Regions = from region in employee.Elements("Region") select new { Name = region.Attribute("name").Value, AreaCode = region.Element("Area").Attribute("code").Value, } } from region in selectedEmployee2.Regions where region.AreaCode == "96" select selectedEmployee2; 

But there is one little thing that you should consider. To ensure reliability, you need to check the presence of your elements and attributes, then the choice will look like this:

  var employees = from employee in xml.Descendants("CompanyInfo").Elements("Employee") select new { EmployeeName = (employee.Attribute("name") != null) ? employee.Attribute("name").Value : string.Empty, EmployeeDeptId = (employee.Attribute("deptId") != null) ? employee.Attribute("deptId").Value : string.Empty, Regions = (employee.Elements("Region") != null)? from region in employee.Elements("Region") select new { Name = (region.Attribute("name")!= null) ? region.Attribute("name").Value : string.Empty, AreaCode = (region.Element("Area") != null && region.Element("Area").Attribute("code") != null) ? region.Element("Area").Attribute("code").Value : string.Empty, } : null }; 
+2
source

Source: https://habr.com/ru/post/954154/


All Articles