How to convert List <Dictionary <string, object >> to List <[new class with dynamic properties]>

I have a project where a class contains List <> Summary objects and each Summary contains a dictionary of semi-dynamic properties. That is, each Summary in the given list will have the same keys in the dictionary. I am using this project to create a set of dynamic "properties" to track the totals, as my project specifications will adjust those totals at runtime.

The question is, how can I flatten this list so that every item in the list is treated as if the keys in the dictionary were valid properties?

I've tried various options for converting a dictionary to a list, but this seems inherently wrong since I really need to treat keys as properties. I am guessing that I need to use the new C # 4.0+ dynamic feature and ExpandoObject, but I cannot get it right.

The code below shows a basic setup where "flattenedSummary" gives me what I want - a new dynamic-type list whose properties are the keys of the Pivot Dictionary. However, this is a disadvantage in that I have hard-coded the property names and I cannot do that because I will not know them until runtime.

The flattened version of Summary2 tries to flatten the list, but it doesn't fit because the return type is still a list, not the list I want.

    public class Summary : Dictionary<string, object>
    {
    }

    public class ClassA
    {
        public List<Summary> Summaries = new List<Summary>();
    }

    static void Main(string[] args)
    {
        ClassA a = new ClassA();
        var summary = new Summary();
        summary.Add("Year", 2010);
        summary.Add("Income", 1000m);
        summary.Add("Expenses", 500m);
        a.Summaries.Add(summary);

        summary = new Summary();
        summary.Add("Year", 2011);
        summary.Add("Income", 2000m);
        summary.Add("Expenses", 700m);
        a.Summaries.Add(summary);

        summary = new Summary();
        summary.Add("Year", 2012);
        summary.Add("Income", 1000m);
        summary.Add("Expenses", 800m);
        a.Summaries.Add(summary);

        var flattenedSummary = from s in a.Summaries select new { Year = s["Year"], Income = s["Income"], Expenses = s["Expenses"] };
        ObjectDumper.Write(flattenedSummary, 1);

        var flattenedSummary2 = Convert(a);
        ObjectDumper.Write(flattenedSummary2, 1);

        Console.ReadKey();
    }

    public static List<ExpandoObject> Convert(ClassA a)
    {
        var list = new List<ExpandoObject>();
        foreach (Summary summary in a.Summaries)
        {
            IDictionary<string, object> fields = new ExpandoObject();
            foreach (var field in summary)
            {
                fields.Add(field.Key.ToString(), field.Value);
            }
            dynamic s = fields;
            list.Add(s);
        }

        return list;
    }

      

+3


source to share


2 answers


Daniel,

I found this article which has a solution that might work for you. http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-extension-method/

So you would create an extension method ...

  public static ExpandoObject ToExpando(this IDictionary<string, object> dictionary)
        {
            var expando = new ExpandoObject();
            var expandoDic = (IDictionary<string, object>)expando;

            // go through the items in the dictionary and copy over the key value pairs)
            foreach (var kvp in dictionary)
            {
                // if the value can also be turned into an ExpandoObject, then do it!
                if (kvp.Value is IDictionary<string, object>)
                {
                    var expandoValue = ((IDictionary<string, object>)kvp.Value).ToExpando();
                    expandoDic.Add(kvp.Key, expandoValue);
                }
                else if (kvp.Value is ICollection)
                {
                    // iterate through the collection and convert any strin-object dictionaries
                    // along the way into expando objects
                    var itemList = new List<object>();
                    foreach (var item in (ICollection)kvp.Value)
                    {
                        if (item is IDictionary<string, object>)
                        {
                            var expandoItem = ((IDictionary<string, object>)item).ToExpando();
                            itemList.Add(expandoItem);
                        }
                        else
                        {
                            itemList.Add(item);
                        }
                    }

                    expandoDic.Add(kvp.Key, itemList);
                }
                else
                {
                    expandoDic.Add(kvp);
                }
            }

            return expando;
        }

      



Then from your main function ...

List<ExpandoObject> flattenSummary3 = new List<ExpandoObject>();
foreach ( var s in a.Summaries)
{
    flattenSummary3.Add(s.ToExpando());
}

      

And now the flattenSummary3 variable will contain a list of ExpandObject objects that you can reference by properties.

Hope this helps.

0


source


While I was accepting Ed's answer as it was very close, I provide the following code in case anyone finds it useful. The key changes were to ensure that all uses of ExpandoObject were set dynamic so that the final list was dynamic. Without these changes, exploring the types inside the list would still return ExpandoObject (for example, json serialization gave ExpandoObject instead of expected property names / values).

First the ToExpando () method (which should probably be called by ToDynamic):

public static dynamic ToExpando(this IDictionary<string, object> dictionary)
{
    dynamic expando = new ExpandoObject();
    var expandoDic = (IDictionary<string, object>)expando;

    // go through the items in the dictionary and copy over the key value pairs)
    foreach (var kvp in dictionary)
    {
        // if the value can also be turned into an ExpandoObject, then do it!
        if (kvp.Value is IDictionary<string, object>)
        {
            var expandoValue = ((IDictionary<string, object>)kvp.Value).ToExpando();
            expandoDic.Add(kvp.Key, expandoValue);
        }
        else if (kvp.Value is ICollection)
        {
            // iterate through the collection and convert any strin-object dictionaries
            // along the way into expando objects
            var itemList = new List<object>();
            foreach (var item in (ICollection)kvp.Value)
            {
                if (item is IDictionary<string, object>)
                {
                    var expandoItem = ((IDictionary<string, object>)item).ToExpando();
                    itemList.Add(expandoItem);
                }
                else
                {
                    itemList.Add(item);
                }
            }

            expandoDic.Add(kvp.Key, itemList);
        }
        else
        {
            expandoDic.Add(kvp);
        }
    }

    return expando;
}

      

And the calling code will look like this:

    List<dynamic> summaries = new List<dynamic>();
    foreach (var s in a.Summaries)
    {
        summaries.Add(s.DynamicFields.ToExpando());
    }

      



Or an even more compact version:

    a.Summaries.Select(s => s.DynamicFields.ToExpando())

      

All of the above provides an object that can be referenced as:

    int year = a.Summaries[0].Year; // Year is a dynamic property of type int
    decimal income = a.Summaries[0].Income; // Income is a dynamic property of type decimal

      

Of course the idea is that I won't know the properties, but they can be serialized to json or, with some tweaking, used to bind a grid or other UI element to display.

0


source







All Articles