在单个 LINQ XML 查询中填充 class 对象的方法?
Way to populate class object in single LINQ XML query?
给定以下 XML 片段,有没有办法在一个 LINQ 语句中查询和填充 class 对象?这很混乱,因为需要 select 使用属性值。
<data>
<array>
<item key="0">
<map>
<item key="mrid">53030</item>
<item key="mrtitle">GeneralFeedback</item>
</map>
</item>
</array>
</data>
Class:
public class Incident
{
public int ID { get; set; }
public string Title { get; set; }
}
当前(工作)代码(其中结果是作为字符串的 XML 片段):
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 数据中存在多个事件。
查看此 post 以了解如何将您的 XML 架构转换为 C# class
Generate C# class from XML
然后您可以使用您的新类型并将您的 XML 反序列化为 class
XmlSerializer serializer = new XmlSerializer(typeof(Incident));
using (StringReader reader = new StringReader(xmlDocumentText))
{
Incident incident= (Incident)(serializer.Deserialize(reader));
}
is there a way to both query and populate a class object in one LINQ statement?
是的,有点……而且它仍然很丑陋。下面的 "single" 多步 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
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"]
});
给定以下 XML 片段,有没有办法在一个 LINQ 语句中查询和填充 class 对象?这很混乱,因为需要 select 使用属性值。
<data>
<array>
<item key="0">
<map>
<item key="mrid">53030</item>
<item key="mrtitle">GeneralFeedback</item>
</map>
</item>
</array>
</data>
Class:
public class Incident
{
public int ID { get; set; }
public string Title { get; set; }
}
当前(工作)代码(其中结果是作为字符串的 XML 片段):
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 数据中存在多个事件。
查看此 post 以了解如何将您的 XML 架构转换为 C# class Generate C# class from XML
然后您可以使用您的新类型并将您的 XML 反序列化为 class
XmlSerializer serializer = new XmlSerializer(typeof(Incident));
using (StringReader reader = new StringReader(xmlDocumentText))
{
Incident incident= (Incident)(serializer.Deserialize(reader));
}
is there a way to both query and populate a class object in one LINQ statement?
是的,有点……而且它仍然很丑陋。下面的 "single" 多步 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
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"]
});