Как мне преобразовать List<Dictionary <string, object >> в List<[новый класс с динамическими свойствами]>
У меня есть дизайн, где класс содержит список<> объектов Summary, а каждый Summary - это словарь полудинамических свойств. То есть каждое резюме в данном списке будет иметь одинаковые ключи в словаре. Я использую этот дизайн для создания набора динамических "свойств" для отслеживания итоговых значений, поскольку, согласно спецификациям моего проекта, эти итоговые значения будут настраиваться во время выполнения.
Вопрос заключается в следующем: как можно сгладить этот список, чтобы каждый элемент в списке обрабатывался так, как если бы ключи в словаре были фактическими свойствами?
Я пробовал разные варианты преобразования словаря в список, но это по своей сути неверно, поскольку мне действительно нужно рассматривать ключи как свойства. Я предполагаю, что мне нужно использовать новую динамическую функцию C# 4.0+ и ExpandoObject, но я не совсем понимаю, как это правильно.
Приведенный ниже код демонстрирует базовую настройку, с "flatenedSummary", которая дает мне то, что я хочу - новый список динамического типа, свойства которого являются ключами словаря Summary. Однако это является недостатком в том, что я жестко запрограммировал имена свойств, и я не могу этого сделать, так как я не буду знать их до момента выполнения.
Версия flateredSummary2 пытается сгладить список, но терпит неудачу, так как возвращаемый тип все еще является списком, а не списком, который мне нужен.
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;
}
2 ответа
Daniel,
Я нашел эту статью, в которой есть решение, которое может работать на вас. http://theburningmonk.com/2011/05/idictionarystring-object-to-expandoobject-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;
}
Тогда из вашей основной функции...
List<ExpandoObject> flattenSummary3 = new List<ExpandoObject>();
foreach ( var s in a.Summaries)
{
flattenSummary3.Add(s.ToExpando());
}
И теперь переменная flattenSummary3 будет содержать список объектов ExpandObject, на которые вы можете ссылаться по свойствам.
Надеюсь, это поможет.
Хотя я принял ответ Эда, так как он был очень близок, я предоставляю следующий код на случай, если кто-то еще посчитает его полезным. Ключевыми изменениями было обеспечение того, чтобы все использования ExpandoObject были установлены как динамические, чтобы окончательный список был динамическим. Без этих изменений при проверке типов внутри списка по-прежнему возвращался ExpandoObject (например, сериализация в json дала ExpandoObject вместо ожидаемых имен / значений свойств).
Во-первых, метод ToExpando() (который, вероятно, следует называть 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;
}
И код вызова будет выглядеть так:
List<dynamic> summaries = new List<dynamic>();
foreach (var s in a.Summaries)
{
summaries.Add(s.DynamicFields.ToExpando());
}
Или еще более компактная версия:
a.Summaries.Select(s => s.DynamicFields.ToExpando())
Все вышеперечисленное предоставляет объект, на который можно ссылаться как:
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
Конечно, идея в том, что я не буду знать свойства - но они могут быть сериализованы в json или, с некоторыми изменениями, использоваться для привязки сетки или другого элемента пользовательского интерфейса для отображения.