For my blazor library which is a modification of this awesome library I have to convert an ExpandoObject
into a Dictionary<string, object>
since ExpandoObject
s aren't serialized properly in the newest preview versions of dotnet-core. See my question related to this for more details.
My current approach goes as follows:
Dictionary<string, object> ConvertDynamicToDictonary(IDictionary<string, object> value)
{
return value.ToDictionary(
p => p.Key,
p =>
{
// if it's another IDict (might be a ExpandoObject or could also be an actual Dict containing ExpandoObjects) just go through it recursively
if (p.Value is IDictionary<string, object> dict)
{
return ConvertDynamicToDictonary(dict);
}
// if it's an IEnumerable, it might have ExpandoObjects inside, so check for that
if (p.Value is IEnumerable<object> list)
{
if (list.Any(o => o is ExpandoObject))
{
// if it does contain ExpandoObjects, take all of those and also go through them recursively
return list
.Where(o => o is ExpandoObject)
.Select(o => ConvertDynamicToDictonary((ExpandoObject)o));
}
}
// neither an IDict nor an IEnumerable -> it's probably fine to just return the value it has
return p.Value;
}
);
}
This works fine but I'm not sure about the IEnumerable
part. If I omit the list.Any
check it still works fine. New version (only write the IEnumerable
part changed):
// if it's an IEnumerable, it might have ExpandoObjects inside, so check for that
if (p.Value is IEnumerable<object> list)
{
// take all ExpandoObjects and go through them recursively
return list
.Where(o => o is ExpandoObject)
.Select(o => ConvertDynamicToDictonary((ExpandoObject)o));
}
I have also tried using IEnumerable<ExpandoObject>
instead of just IEnumerable<object>
to see if that would work and maybe be cleaner and I can confirm that that does not work (throws error).
What I would like to know now is, if it's a good idea to omit the Any
check and which option is more performant (and why) and/or cleaner.
Also please mention every small thing that bugs you, I always want to make it as perfect as possible :)
I first wanted to post this on StackOverflow but I think it fits better on here - correct me if I'm wrong.
EDIT 1:
Because I feel like I have not made clear how I will use this function, I have some demo-code for you. This will of course not compile, it's just to show how and why I need this method.
If this edit is problematic because I'm adding new code, I will post a new, more complete question later on (I sadly don't have anymore time today).
// this class (like all the others) are of course much more complicated than this.
// There's also a lot of abstraction etc (you can see the actual code on the github I've linked at the start).
SomeConfig config = new SomeConfig
{
Options = new SomeOptions
{
SomeInt = 2,
SomeString = null, // any trace of this property will not be present in the expando object
Axes = new List<Axis>
{
new Axis() // this axis will also be an expandoObject after being extracted from the json
}
},
Data = new SomeData
{
Data = new List<int> { 1, 2, 3, 4, 5 },
SomeString = "asdf"
}
};
dynamic expando = /* ParseConfigToJsonWithoutNulls -> ParseJsonToExpandoObject */
IJSRuntime jsRT = GetTheRuntime();
jsRT.InvokeAsync("blabla", expando); // this would NOT work because ExpandoObject cannot be serialized to json correctly and throws a runtime error
// this method is what we need in the best way possible without missing something :)
Dictionary<string, object> dictFromExpando = ConvertExpandoToDictonary(expando);
// so the only purpose of the ConvertExpandoToDictonary is to recursively convert all the ExpandoObjects to Dictionaries as they are serializable
// I have to do this recursively since there are nested ExpandoObjects that were created when parsing the huge json to the ExpandoObject with Json.Net
// I have decided to go with Dictionaries because it works and ExpandoObject implements the interface IDictionary<string, object>
jsRT.InvokeAsync("blabla", dictFromExpando); // this now works perfectly fine
EDIT 2:
I have asked a new, much more detailed question. Please have a look at it if you have the time :)