How to get attribute value using LINQ to XML?

2019-03-19 06:05发布

问题:

<Employees>
  <Employee>
    <EmpId>1</EmpId>
    <Name>Sam</Name>
    <Sex>Male</Sex>
    <Phone Type="Home">423-555-0124</Phone>
    <Phone Type="Work">424-555-0545</Phone>
  </Employee>
</Employees>

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    emplyeeDetails = XDocument.Load(Directory.GetParent(Directory.GetCurrentDirectory()).Parent.FullName + "\\LinqToXml\\Xmls\\" + "Employees.xml");
    var emplyees = from emp in emplyeeDetails.Descendants("Employee").Take(10)
                   orderby emp.Element("EmpId").Value ascending
                   select new
                   {
                       Id = emp.Element("EmpId").Value,
                       Name = emp.Element("Name").Value,
                       Sex = emp.Element("Sex").Value,
                       WorkPhone=emp.Element("Phone").Attribute("Type").Value,
                       HomePhone = emp.Element("Phone").Attribute("Type").Value,                               
                   };
    DgrdEmployeeDetails.ItemsSource = emplyees.ToList();
}

Using the code above, I can get the result below:

But I need the column WorkPhone's value 424-555-0545 instead of Home and the column HomePhone's value 423-555-0124 instead of Home.

What should I do for that?

回答1:

Use the Where method:

For the Home phone number:

emp.Elements("Phone").Single(phoneElement => phoneElement.Attribute("Type").Value == "Home").Value

For the Work phone number:

emp.Elements("Phone").Single(phoneElement => phoneElement.Attribute("Type").Value == "Work").Value
  • emp.Elements("Phone") is a enumerable on all "Phone" elements of emp.
  • Single will get the element that satisfy the specified property (if there are 0 or more than 1 element that satisfy the property, an error is raised).
  • phoneElement.Attribute("Type").Value is the value of the attribute "Type" (ie "Home" or "Work")

Then, your code should be:

var emplyees = from emp in emplyeeDetails.Descendants("Employee").Take(10)
                orderby emp.Element("EmpId").Value ascending
                select new
                {
                    Id = emp.Element("EmpId").Value,
                    Name = emp.Element("Name").Value,
                    Sex = emp.Element("Sex").Value,
                    WorkPhone = emp.Elements("Phone").Single(phoneElement => phoneElement.Attribute("Type").Value == "Home").Value,
                    HomePhone = emp.Elements("Phone").Single(phoneElement => phoneElement.Attribute("Type").Value == "Work").Value,
                };

If the element emp may have no Work phone or Home phone number, the above code will raise an exception in the Single. To deal with this case you have to change your code to:

(string)emp.Elements("Phone").SingleOrDefault(phoneElement => phoneElement.Attribute("Type").Value == "Home")

SingleOrDefault will equal null if no "Phone" element satisfy the condition and the string cast on a XElement is equivalent to XElement.Value.



回答2:

This code will work even if there is any Phone elements exist for employee:

var emplyees = 
    from emp in emplyeeDetails.Descendants("Employee").Take(10)
    let phones = emp.Descendants("Phone")
    orderby (int)emp.Element("EmpId")
    select new
    {
        Id = (int)emp.Element("EmpId"),
        Name = (string)emp.Element("Name"),
        Sex = (string)emp.Element("Sex"),
        WorkPhone = (string)phones.FirstOrDefault(p => (string)p.Attribute("Type") == "Work"),
        HomePhone = (string)phones.FirstOrDefault(p => (string)p.Attribute("Type") == "Home")                               
    };

Use casting elements to string, int, etc instead of accessing Value property. Why? Because if there is some missing element or attribute in your xml, then you will get a NullReferenceException. But casting will return default value instead. So, code above will parse even xml like this:

<Employees>
  <Employee>
    <EmpId>1</EmpId>
    <Name>Sam</Name>
    <Phone Type="Home">423-555-0124</Phone>
    <Phone>524-777-1234</Phone>
  </Employee>
</Employees>