I'm trying to serialize a Lookup into JSON by doing:
Lookup<string, string> productIdsByCategory = prodcuts.ToLookup(p => p.Category, p => p.Id);
Then in my ASP.NET Core WebAPI controller i'm returning by doing:
var response = new ComplexType
{
PropertyA = productIdsByCategory
... other properties left out ...
};
return Json(response)
The response in Postman from my WebAPI look like this:
{
"PropertyA": [
[
"ProductId_A"
],
[
"ProductId_B"
],
[
"ProductId_C"
]
],
... other properties left out ...
}
The keys are not included in the JSON response, is it even possible to do this?
You're right in that Newtonsoft.Json doesn't treat Lookup<TKey, TElement>
nicely. The following code:
var input = new List<string>()
{
"A1",
"A2",
"B1",
"C1",
"C2"
};
var lookup = input.ToLookup(i => i[0], i => i);
var json = JsonConvert.SerializeObject(lookup);
Console.WriteLine(json);
Serializes as:
[["A1","A2"],["B1"],["C1","C2"]]
You correctly notice the key names (A
, B
, C
) are missing, it appears to be serialized as an array of arrays.
There appears to be no support for serializing a lookup out of the box, so you'll have to write your own converter.
I found one here, but that requires you to know the type of key and value on beforehand.
Modified it, this does the job:
// Same as above, up till here
var json = JsonConvert.SerializeObject(lookup, new LookupSerializer());
Console.WriteLine(json);
}
}
public class LookupSerializer : JsonConverter
{
public override bool CanConvert(Type objectType)
{
var result = objectType.GetInterfaces().Any(a => a.IsGenericType
&& a.GetGenericTypeDefinition() == typeof(ILookup<,>));
return result;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var obj = new JObject();
var enumerable = (IEnumerable)value;
foreach (object kvp in enumerable)
{
// TODO: caching
var keyProp = kvp.GetType().GetProperty("Key");
var keyValue = keyProp.GetValue(kvp, null);
obj.Add(keyValue.ToString(), JArray.FromObject((IEnumerable)kvp));
}
obj.WriteTo(writer);
}
}
Output:
{
"A": ["A1", "A2"],
"B": ["B1"],
"C": ["C1", "C2"]
}
As a workaround to creating a custom converter, you could convert your Lookup<TKey, TElement>
into a Dictionary<TKey, List<TElement>>
which Newtonsoft.Json plays nice with.
myLookup.ToDictionary(x => x.Key, x => x.ToList());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With