Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert KeyValuePair to anonymous type in a LINQ query

Tags:

json

c#

.net

linq

I have an IEnumerable<KeyValuePair<string,string>>, from which I would like, ideally, an anonymous object which has the keys as property names and the values as property values.

I've tried various selection expressions (none of which even compiled...) and an approach using ExpandoObject (see below), but without success. Is there a good way to do this? If possible, I'd like to avoid an extra explicit iteration over the collection (i.e. do it all with a LINQ statement of some sort).

This is what I've tried so far. I hope it also clarifies what I'm trying to do:

var kvps = getProps(); // returns IEnumerable<KeyValuePair<string,string>>

dynamic o = new ExpandoObject();
foreach (var kvp in kvps)
{
    o.Add(kvp);
}

This is OK at compile time, but at runtime I get a YSOD stating 'System.Dynamic.ExpandoObject' does not contain a definition for 'Add' - I guess it works at compile time because o is dynamic, so the compiler can't know if a method .Add() has been added to it since it was instantiated. The odd thing is, that on the MSDN documenation page for ExpandoObject .Add() is listed as one of several "explicitly implemented interface methods".

It is not necessary for me to get this into a dynamic object - I just need to get something that has property names and values according to the keys and values of the key-value pairs.

Update: Well, this is embarrassing. Turns out this was something of an XY-problem too.

I'm trying to render this to JSON using the built-in features of ASP.NET MVC, by simply returning Json(data) in my controller. The answers all worked very well to do what I first asked, but when I pass this object as data I still don't get what I want:

// What I get:
[
   { Key: 'firstkey', Value: 'FirstValue' }, 
   { Key: 'secondKey', Value: 'secondValue' } 
]

// What I want:
{ firstKey: 'FirstValue', secondKey: 'secondValue' }

Apparently, an ExpandoObject with the relevant properties added didn't cut it - it was cast to a dictionary before rendering...

like image 399
Tomas Aschan Avatar asked Jun 25 '12 14:06

Tomas Aschan


1 Answers

You need to use the ExpandoObject as an IDictionary<string, object> while populating it:

var kvps = getProps(); // returns IEnumerable<KeyValuePair<string,string>>

IDictionary<string, object> o = new ExpandoObject();
foreach (var kvp in kvps)
{
    // Or use Add(kvp.Key, kvp.Value), if you want
    o[kvp.Key] = kvp.Value;
}

dynamic d = o;
// Now you can use the properties
like image 166
Jon Skeet Avatar answered Nov 14 '22 23:11

Jon Skeet