Reputation: 583
I have an object model with a large number of properties. The values of these properties are extracted from a database to give an IEnumerable
list or array like this:
var obj = context.Model.Where(x => idList.Contains(x.Id)).ToList();
This gives a Json output blob in this structure:
[{ Prop1: 57, Prop2: 2, Prop3: 25 ... },
{ Prop1: 23, Prop2: 4, Prop3: 20 ....},
{ Prop1: 15, Prop2: 6, Prop3: 32 ....},
... ]
Is there a way I can set up the linq query to extract the data in this form:
{ Prop1: [57,23,15, ...],
Prop2: [2,4,6, ....],
Prop3: [25,20,32, ...],
... }
In other words I want collection of object arrays not an array of objects
Upvotes: 1
Views: 1934
Reputation: 2086
I don't think you can do this with purly Linq if you want to be generic. You need to use at least some reflection to loop over your properties.
The following code should do what yo want:
var list = new List<object> {new { A = 1, B = 2, C = 3}, new {A = 1, B = 1, D = 1}};
var result = new ExpandoObject();
var list1 = list.Aggregate<object, ExpandoObject>(result, (res, a) =>
{
foreach (var prop in a.GetType().GetProperties())
{
object val = prop.GetValue(a);
var x = res as IDictionary<string, Object>;
object o;
if (!x.TryGetValue(prop.Name, out o))
{
o = new List<object>();
x.Add(prop.Name, o);
}
((List<object>)o).Add(val);
}
return res;
});
var inputJson = Newtonsoft.Json.JsonConvert.SerializeObject(list);
var outputJson = Newtonsoft.Json.JsonConvert.SerializeObject(list1);
For this input: [{"A":1,"B":2,"C":3},{"A":1,"B":1,"D":1}]
It gives the following output: {"A":[1,1],"B":[2,1],"C":[3],"D":[1]}
Of course, if you have strongly typed classes you don't need to use reflection. You can also pass the class types to aggregate and write the mapping on your own.
Upvotes: 1
Reputation: 116532
If you are using Json.NET, you can use LINQ to JSON to restructure the JSON in a completely generic way without needing to write your own reflection code:
var jArray = JArray.FromObject(obj); // obj must serialized to an array; throw an exception otherwise.
var jObj = new JObject(jArray // Allocate new outer JSON object
.Cast<JObject>() // All array element must be json objects
.SelectMany(o => o.Properties())
.GroupBy(p => p.Name, p => p.Value) // Group all array element properties by name
.Select(g => new JProperty(g.Key, g))); // Add an array-valued property to the new outer object.
var json = jObj.ToString();
Debug.WriteLine(json);
Given the following input obj
:
var obj = new List<object>
{
new { Prop1 = 57, Prop2 = 2, Prop3 = 25 },
new { Prop1 = 23, Prop2 = 4, Prop3 = 20 },
new { Prop1 = 15, Prop2 = 6, Prop3 = 32 },
};
The following JSON is produced:
{"Prop1":[57,23,15],"Prop2":[2,4,6],"Prop3":[25,20,32]}
Alternatively, if your obj
is strongly typed, you can manually create an intermediate anonymous type for output, like so:
var newObj = new { Prop1 = obj.Select(i => i.Prop1), Prop2 = obj.Select(i => i.Prop2), Prop3 = obj.Select(i => i.Prop3) };
Then, given the following input obj
:
var obj = new[]
{
new [] { 57,2,25 },
new [] { 23,4,20 },
new [] { 15,6,32 },
}
.Select(a => new { Prop1 = a[0], Prop2 = a[1], Prop3 = a[2] });
The same JSON is produced.
Upvotes: 2