简体   繁体   English

Linq to XML:使用元素属性和值创建一个匿名对象

[英]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. 我是Linq的新手,正在尝试查询XML文档以查找特定用户的客户经理列表。 (I realize it might make more sense to put this in a database or something else, but this scenario calls for a XML document). (我意识到将其放入数据库或其他内容可能更有意义,但是这种情况需要使用XML文档)。

<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). 我试图创建具有XElement属性(部门,标题)和值(电子邮件)组成的属性的对象列表(匿名类型?)。 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. 我可以在a.Valuea.Attribute ,但我无法弄清楚如何让两者并将其存储在一个对象。 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: 如果要从Select方法返回匿名类型,可以编写:

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. 创建匿名类型的语法并不直接绑定到select关键字。 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. 因此,语法a => new { ... }是返回新匿名类型的lambda表达式,就像select new { ... }是构造新匿名类型的子句一样。

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). 关于在点符号和查询语法之间进行选择-这确实是个人喜好(尽管使用查询语法时某些情况看起来更好,并且诸如Count某些方法只能使用点符号来使用)。 However, all queries all translated to method calls by the compiler. 但是,所有查询都由编译器转换为方法调用。

I assume that there's only a single user with sipAddress == sipUri ? 我假设只有一个用户sipAddress == sipUri

Let's start with this: 让我们从这个开始:

var managers = _xDoc.Root

You can select just the first element that match by using .First 您可以使用.First仅选择匹配的第一个元素.First

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

This will gets the first user where the predicate returns true. 这将使第一个用户谓词返回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 如果有很多用户,并且您想要列出的所有经理,则可以使用.SelectMany (即,对于每个用户,有很多经理,选择所有经理)来完成此操作,但在这种情况下,我假设只有一个,并且.First已返回该元素,因此您只需使用其属性即可访问经理列表

.Descendants("accountManager")

Then you'll get all the XElement descending from the selected element. 然后,您将获得所有从选定元素降序的XElement Transforming it into another object by using the values from the elements is done by using a mapping function which, in Linq, is .Select 使用元素中的值将其转换为另一个对象是通过使用映射函数完成的,在Linq中是.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 . 如前所述,如果有多个用户使用正确的sipAddress并且您希望所有管理者,只需将.Descendants包装在.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. :)随时在评论中询问您是否仍然感到困惑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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