C# 使用 LINQ 解析 XML 以获取子元素
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18891206/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Parse XML with LINQ to get child elements
提问by 03Usr
<?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, so far I have tried this:
我正在尝试读取此 XML 数据,到目前为止我已经尝试过:
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 enables me to get each node, its attribute name and value and so I can save these data into the relevant field in database, but this seems a long winded way and not flexible, for instance if the XML structure changes all those foreach statements needs revisiting, also it is difficult to filter the data this way, I need to write certain if statements to filter the data (e.g get employees from West only etc...)
这使我能够获取每个节点、其属性名称和值,因此我可以将这些数据保存到数据库中的相关字段中,但这似乎是一种冗长且不灵活的方式,例如,如果 XML 结构更改了所有这些 foreach 语句需要重新审视,也很难以这种方式过滤数据,我需要编写某些 if 语句来过滤数据(例如,仅从 West 获取员工等...)
I was looking for a more flexible way, using linq, something like this:
我正在寻找一种更灵活的方式,使用 linq,如下所示:
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 am not sure how I can get the values from the child nodes and apply the filtering (to get the certain employees only). Is this possible? Any help is appreciated.
但我不确定如何从子节点获取值并应用过滤(仅获取某些员工)。这可能吗?任何帮助表示赞赏。
Thanks
谢谢
采纳答案by MarcinJuraszek
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();
But it will still require query revision when XML file structure changes.
但是当 XML 文件结构发生变化时,它仍然需要查询修改。
Edit
编辑
Query for multiple 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();
You can then filter the list for employees from given region only:
然后,您可以仅过滤给定区域的员工列表:
var westEmployees = employees.Where(x => x.RegionList.Any(r => r.RegionName == "West")).ToList();
回答by Henk Holterman
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 want:
然后得到你想要的信息:
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 the additional info about multiple regions, assuming a List<Region> Regions
property:
以及关于多个区域的附加信息,假设一个List<Region> Regions
属性:
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();
}
回答by Rami Yampolsky
You can do the selection in one query and then the filtering in second or combine them both to one query:
您可以在一个查询中进行选择,然后在第二个查询中进行过滤,或者将它们组合到一个查询中:
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 one query (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 you should consider adding. For robustness, you need to check existence of your elements and attributes then the selection will look like that:
但是您应该考虑添加一件小事。为了健壮性,您需要检查元素和属性是否存在,然后选择将如下所示:
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
};