简体   繁体   中英

Linq to XML: create an anonymous object with element attributes and values

I'm new to Linq and I'm trying to query a XML document to find a list of account managers for a particular user. (I realize it might make more sense to put this in a database or something else, but this scenario calls for a XML document).

<user emailAddress='user@fabrikam.com'>
    <accountManager department='Customer Service' title='Manager'>manager@fabrikam.com</accountManager>
    <accountManager department='Sales' title='Account Manager'>manager@fabrikam.com</accountManager>
    <accountManager department='Sales' title='Account Manager'>manager@fabrikam.com</accountManager>
</user>

I trying to create a list of objects (anonymous type?) with properties consisting of both XElement attributes (department, title) and values (email). I know that I can get either of the two, but my problem is selecting both.

Here is what I'm trying:

var managers = _xDoc.Root.Descendants("user")
               .Where(d => d.Attribute("emailAddress").Value == "user@fabrikam.com")
               .SelectMany(u => u.Descendants("accountManager").Select(a => a.Value));

foreach (var manager in managers)
{
     //do stuff
}

I can get at a.Value and a.Attribute but I can't figure out how to get both and store them in an object. I have a feeling it would wind up looking something like:

select new { 
    department = u.Attribute("department").Value,
    title = u.Attribute("title").Value,
    email = u.Value
};

You are correct. It would look exactly like that.

For example:

_xDoc.Root.Descendants("user")
          .Where(d => d.Attribute("emailAddress").Value == "user@fabrikam.com")
          .SelectMany(u => u.Descendants("accountManager"))
          .Select(a => new { 
              department = a.Attribute("department").Value,
              title = a.Attribute("title").Value,
              email = a.Value
          });

EDIT : Using query comprehension syntax:

from u in _xDoc.Root.Descendants("user")
where u.Attribute("emailAddress").Value == "user@fabrikam.com"
from a in u.Descendants("accountManager")
select new { 
               department = a.Attribute("department").Value,
               title = a.Attribute("title").Value,
               email = a.Value
           });

If you want to return anonymous type from a Select method you can write:

var managers = 
  _xDoc.Root.Descendants("user") 
       .Where(d => d.Attribute("emailAddress").Value == "user@fabrikam.com") 
       .SelectMany(u => 
          u.Descendants("accountManager")
           .Select(a => new {  
              Department = u.Attribute("department").Value, 
              Title = u.Attribute("title").Value, 
              Email = u.Value })
        );

The syntax for creating anonymous types isn't directly bound to the select keyword. You can use it for example like this:

var manager = new { Department = "Somewhere", Title = "Mr" };

So, the syntax a => new { ... } is a lambda expression that returns a new anonymous type, just like select new { ... } is a clause that constructs new anonymous type.

Regarding the choice between dot-notation and query syntax - this is really a personal preference (although some things look better with the query syntax and some methods such as Count can be used only using dot-notation). However, all queries all translated to method calls by the compiler.

I assume that there's only a single user with sipAddress == sipUri ?

Let's start with this:

var managers = _xDoc.Root

You can select just the first element that match by using .First

.First(d => d.Attribute("sipAddress").Value == sipUri)

This will gets the first user where the predicate returns true. Then, you'll want to extract both the attributes and the values in the element into a new object (anonymous type).

If there're many users and you want all the listed managers, this can be done by using .SelectMany (ie for each users, there're many managers, select all of them) but in this case I assume there's only one, and .First has returned that very element so you can just use its properties to access the list of managers

.Descendants("accountManager")

Then you'll get all the XElement descending from the selected element. Transforming it into another object by using the values from the elements is done by using a mapping function which, in Linq, is .Select

.Select(managerElem => new {
    department = u.Attribute("department").Value,
    title = u.Attribute("title").Value,
    email = u.Value 
});

As I've said earlier, if there're multiple users with the correct sipAddress and you want all the managers, just wrap the .Descendants inside .SelectMany .

.SelectMany(userElem => userElem.Descendants("accountManager"))

I havn't tested the code but I believe you'll be able to figure it out by following what I've written.

Hope this helps! :) Feel free to ask in the comments if you're still confused.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM