如何在条件基础上将XML文件处理成C#List <t>?

时间:2016-08-30 10:35:00

标签: c# xml linq-to-xml

这是我的SQL Server生成的示例XML架构。我希望它使用LINQ在C#中分配给List<class>。我尝试了但是我得到了六行但是在解析了这个XML之后。我应该只列出一行。

有一些条件可以解析这个XML。

  • TYPE == 1然后是短信,
  • TYPE == 3然后是DATA
  • TYPE == 2 then MINUTES

XML:

<summary>
    <period>Jul-2016</period>
    <providerid>7</providerid>
    <type>1</type>
    <volume>2981655</volume>          
</summary>
<summary>
    <period>Jul-2016</period>
    <providerid>7</providerid>
    <type>2</type>
    <volume>6449570</volume>
</summary>
<summary>
    <period>Jul-2016</period>
    <providerid>7</providerid>
    <type>3</type>
    <volume>7702484</volume>
</summary>        

这是我的C#类结构。我想将此XML架构解析为List<UsageSummary>。解析完后,List<UsageSummary>只会有两行。我使用XDocument.Parse进行XML解析。之后,我使用Linq .Descendants方法,但我得到六行,但输出应该只包含一行。

public class UsageSummary
{       
        public int carrierID { get; set; }
        public Int64 minutes { get; set; }
        public Int64 sms { get; set; }
        public Int64 data { get; set; }
        public string period { get; set; }
}

我使用过这段代码:

List<UsageSummary> obj = new List<UsageSummary>();

obj = (from res in xmlDoc.Descendants("summary")
       select new UsageSummary
                  {
                      carrierID = (Convert.ToInt16(res.Element("providerid").Value)),                 
                      period = res.Element("period").Value.ToString(),
                      sms = (Convert.ToInt64(res.Element("SMS").Value)),
                      data = (Convert.ToInt64(res.Element("DATA").Value)),
                      minutes = (Convert.ToInt64(res.Element("MINUTES").Value))
                  }).ToList();    

请帮我解决这个问题。

2 个答案:

答案 0 :(得分:1)

您需要对元素进行分组。您可以将问题分成3个部分,这样您就不会一次尝试这个问题。

首先,将XML映射到镜像XML结构的结构:

var items =
    from summary in doc.Descendants("summary")
    select new
    {
       Period = (string) summary.Element("period"),
       CarrierId = (int) summary.Element("providerid"),
       Type = (int) summary.Element("type"),
       Volume = (long) summary.Element("volume")
    };

然后按CarrierIdPeriod对这些进行分组。对于Volume,您希望按Type查找所有卷:

var volumesByPeriodAndCarrier =
    from item in items
    group item by new {item.CarrierId, item.Period}
    into grouping
    select new
    {
        grouping.Key.CarrierId,
        grouping.Key.Period,
        VolumeByType = grouping.ToLookup(x => x.Type, x => x.Volume)
    };

然后您可以轻松创建摘要。 CarrierIdPeriod直接映射到您的班级,3个卷是您使用从类型到字段的映射并汇总总数的地方:

var summaries = volumesByPeriodAndCarrier
    .Select(x => new UsageSummary
    {
        carrierID = x.CarrierId,
        minutes = x.VolumeByType[2].Sum(),
        sms = x.VolumeByType[1].Sum(),
        data = x.VolumeByType[3].Sum(),
        period = x.Period
    }).ToList();

有关正常工作的演示,请参阅this fiddle

答案 1 :(得分:1)

此解决方案使用linq“pivoting”技术实现。

透视包括收集不同类型的数据,每个数据对应于一个记录(或对象..)的字段中的给定数据记录(或我们示例中的xml节点)。

在我们的例子中,每个数据透视对象都包含原始数据集的特定分组的不同类型数据的“集合”(您可以在其上操作多个聚合运算符)。

特别是,它允许volumetypeprovideridperiod值进行求和,并且对于缺失{{0,它会得到零(type) 1}} xml元素值。

List<UsageSummary> obj = new List<UsageSummary>();

obj = (from xmlsummary in doc.Descendants("summary")
       group xmlsummary by new { id = xmlsummary.Element("providerid").Value, period = xmlsummary.Element("period").Value } into summaryGrouped
       select new UsageSummary{
                carrierID = Convert.ToInt16(summaryGrouped.Key.id),
                period = summaryGrouped.Key.period,
                sms = (summaryGrouped.Where(sg => sg.Element("type").Value.Equals("1")).Select(v => Convert.ToInt64(v.Element("volume").Value)).Sum()),
                minutes = (summaryGrouped.Where(sg => sg.Element("type").Value.Equals("2")).Select(v => Convert.ToInt64(v.Element("volume").Value)).Sum()),
                data = (summaryGrouped.Where(sg => sg.Element("type").Value.Equals("3")).Select(v => Convert.ToInt64(v.Element("volume").Value)).Sum())
               }
      ).ToList();