【问题标题】:Way to populate class object in single LINQ XML query?在单个 LINQ XML 查询中填充类对象的方法?
【发布时间】:2015-07-24 00:04:25
【问题描述】:

鉴于以下 XML sn-p,有没有办法在一个 LINQ 语句中查询和填充类对象?由于需要使用属性值进行选择,因此令人困惑。

<data>
 <array>
  <item key="0">
   <map>
    <item key="mrid">53030</item>
    <item key="mrtitle">GeneralFeedback</item>
   </map>
  </item>
 </array>
</data>

类:

public class Incident
{
    public int ID { get; set; }
    public string Title { get; set; }
}

当前(工作)代码(其中结果是作为字符串的 XML sn-p):

var data = XDocument.Parse(result);

var id = from item in data.Descendants("item")
    where item.Attribute("key").Value == "mrid"
    select item.Value;

var title = from item in data.Descendants("item")
    where item.Attribute("key").Value == "mrtitle"
    select item.Value;

var incident = new Incident
{
    ID = Convert.ToInt32(id.FirstOrDefault()),
    Title = title.FirstOrDefault()
};

根据给出的答案,我学到了一些有用的东西并想出了这个变体:

var incidents = data.Descendants("map")
    .Select(i => i.Descendants("item")
        .ToDictionary(m => m.Attribute("key").Value, m => m.Value))
    .Where(i => i.ContainsKey("mrid")
                && i.ContainsKey("mrtitle"))
    .Select(i => new Incident
    {
        ID = int.Parse(i["mrid"]),
        Title = i["mrtitle"]
    });

我真正喜欢的一件事是,它创建了一个 IEnumerable,它允许 XML 数据中存在多个事件。

【问题讨论】:

    标签: c# xml linq linq-to-xml


    【解决方案1】:

    Alex 已经给出了一个完美的答案,但我觉得这更具可读性(: Where 子句确保找到的每个项目都具有构造事件所需的键。

    var incidents = xdoc.Root
                        .Element("array")
                        .Elements("item")
                        .Select(i => i.Element("map")
                                      .Elements("item")
                                      .ToDictionary(m => m.Attribute("key").Value,
                                                    m => m.Value))
                        .Where(i => i.ContainsKey("mrid")
                                 && i.ContainsKey("mrtitle"))
                        .Select(i => new Incident
                                     {
                                         ID = int.Parse(i["mrid"]),
                                         Title = i["mrtitle"]
                                     });
    

    【讨论】:

    • 我选择了@Alex 的答案,因为它为我指明了正确的方向。但结合这一点,我更好地理解可以做什么。我特别感谢强调确保每个项目都存在。我根据两个答案为我的问题添加了一个变体。谢谢。
    【解决方案2】:

    有没有办法在一个 LINQ 语句中同时查询和填充类对象?

    是的,好吧……它仍然很丑陋。下面的“单”多步 LINQ 语句确保仅选择属于同一 map 元素的项目。就像您的代码示例一样,如果缺少具有所需键值的项目(或者“mrid”元素不是int),它将在您的脸上炸开。

    var key_vals = new List<string> { "mrid", "mrtitle" };
    var xdoc = XDocument.Load(@"c:\temp\test.xml");
    var incidents = xdoc.Descendants("map").Select(map => {
        var items = map.Descendants("item").Where(i => key_vals.Contains(i.Attribute("key").Value));
        var idItem = items.Where(x => x.Attribute("key").Value == "mrid").First();
        var titleItem = items.Where(x => x.Attribute("key").Value == "mrtitle").First();
        return new Incident {
            ID = int.Parse(idItem.Value),
            Title = titleItem.Value
        };
    });
    
    foreach (var i in incidents)
        Console.WriteLine("ID = {0}, Title = {1}", i.ID, i.Title);
    

    它将为您给定的 xml 输入文件生成以下输出:

    ID = 53030, Title = GeneralFeedback
    

    【讨论】:

    • 我觉得这很有用。它更具体,向我展示了如何从 LINQ 查询中返回类对象。
    • 在这种情况下,如果不需要: var idItem = items.First(x => x.Attribute("key").Value == "mrid");链接:rextester.com/WBMWS91290
    【解决方案3】:

    查看这篇文章以了解如何将您的 XML 架构转换为 C# 类 Generate C# class from XML

    然后你可以使用你的新类型并将你的 XML 反序列化为一个类

    XmlSerializer serializer = new XmlSerializer(typeof(Incident));
    using (StringReader reader = new StringReader(xmlDocumentText))
    {
        Incident incident= (Incident)(serializer.Deserialize(reader));
    }
    

    【讨论】:

    • 有趣。我试过了,但遇到了错误。尝试了一些方法来克服这个问题,但同样的错误({“ 不是预期的)......这很奇怪,因为没有使用。
    猜你喜欢
    • 2016-03-03
    • 1970-01-01
    • 2016-07-06
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多