Ramdeo angh Ramdeo angh - 3 months ago 10
C# Question

How to process XML file into C# List<T> on conditional basis?

Here is my sample XML schema that is generated by SQL Server. I want it to assign to

List<class>
in C# using LINQ. I tried it but I am getting six rows but after parsing this XML. I should get only one rows in list.

There some conditions to parse this XML.


  • TYPE == 1 then SMS,

  • TYPE == 3 then 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>


Here is my C# class structure. I want to parse this XML schema into a
List<UsageSummary>
. After parsing this there will be only two rows into
List<UsageSummary>
. I have used
XDocument.Parse
for XML parsing. After that, I am using Linq
.Descendants
method but I am getting six rows but output should contain only one rows.

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; }
}


I have used this code:

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();


Kindly help me with this problem.

Answer

You need to group your elements. You can split the problem into 3 parts so you're not trying to this all at once.

First, map your XML onto a structure that mirrors the XML structure:

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")
    };

Then group these by CarrierId and Period. For Volume, you want a dictionary of all the volumes by 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.ToDictionary(x => x.Type, x => x.Volume)
    };

Then you can easily create your summaries. CarrierId and Period map directly to your class, and the 3 volumes are where you use your mapping from types to fields:

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

See this fiddle for a working demo.