How do I reflect over the members of dynamic object?
Asked Answered
G

5

147

I need to get a dictionary of properties and their values from an object declared with the dynamic keyword in .NET 4? It seems using reflection for this will not work.

Example:

dynamic s = new ExpandoObject();
s.Path = "/Home";
s.Name = "Home";

// How do I enumerate the Path and Name properties and get their values?
IDictionary<string, object> propertyValues = ???
Garibold answered 14/4, 2010 at 4:20 Comment(0)
C
34

If the IDynamicMetaObjectProvider can provide the dynamic member names, you can get them. See GetMemberNames implementation in the apache licensed PCL library Dynamitey (which can be found in nuget), it works for ExpandoObjects and DynamicObjects that implement GetDynamicMemberNames and any other IDynamicMetaObjectProvider who provides a meta object with an implementation of GetDynamicMemberNames without custom testing beyond is IDynamicMetaObjectProvider.

After getting the member names it's a little more work to get the value the right way, but Impromptu does this but it's harder to point to just the interesting bits and have it make sense. Here's the documentation and it is equal or faster than reflection, however, unlikely to be faster than a dictionary lookup for expando, but it works for any object, expando, dynamic or original - you name it.

Countrywide answered 26/10, 2011 at 1:1 Comment(3)
Thanks to get to the point the code is: var tTarget = target as IDynamicMetaObjectProvider; if (tTarget !=null) { tList.AddRange(tTarget.GetMetaObject(Expression.Constant(tTarget)).GetDynamicMemberNames()); }Garibold
thanks for your great library. I was having issues round-tripping a dynamic object to this library dynamicjson.codeplex.com, and was able to extended it with your library.Karissakarita
example please.Agglutinin
F
116

In the case of ExpandoObject, the ExpandoObject class actually implements IDictionary<string, object> for its properties, so the solution is as trivial as casting:

IDictionary<string, object> propertyValues = (IDictionary<string, object>)s;

Note that this will not work for general dynamic objects. In these cases you will need to drop down to the DLR via IDynamicMetaObjectProvider.

Flambeau answered 14/4, 2010 at 4:49 Comment(3)
Thanks for that, unfortunately the sample was a little oversimplified. I need to be able to inspect a dynamic object without knowing what it's real type is.Garibold
This only works for objects of the ExpandoObject class, the DynamicObject class is another extensible class which doesn't implement IDictionary but rather implements IDynamicMetaObjectProvider.Parkin
It does answer the OP's question though.Parkin
A
61

There are several scenarios to consider. First of all, you need to check the type of your object. You can simply call GetType() for this. If the type does not implement IDynamicMetaObjectProvider, then you can use reflection same as for any other object. Something like:

var propertyInfo = test.GetType().GetProperties();

However, for IDynamicMetaObjectProvider implementations, the simple reflection doesn't work. Basically, you need to know more about this object. If it is ExpandoObject (which is one of the IDynamicMetaObjectProvider implementations), you can use the answer provided by itowlson. ExpandoObject stores its properties in a dictionary and you can simply cast your dynamic object to a dictionary.

If it's DynamicObject (another IDynamicMetaObjectProvider implementation), then you need to use whatever methods this DynamicObject exposes. DynamicObject isn't required to actually "store" its list of properties anywhere. For example, it might do something like this (I'm reusing an example from my blog post):

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

In this case, whenever you try to access a property (with any given name), the object simply returns the name of the property as a string.

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

So, you don't have anything to reflect over - this object doesn't have any properties, and at the same time all valid property names will work.

I'd say for IDynamicMetaObjectProvider implementations, you need to filter on known implementations where you can get a list of properties, such as ExpandoObject, and ignore (or throw an exception) for the rest.

Accordion answered 21/4, 2010 at 20:39 Comment(3)
It just seems to me that if the type I consume is Dynamic then I shouldn't be making assumptions about it's underlying type. I should be able to call GetDynamicMemberNames to get the list of members. It seems stupid that static types have better runtime inspection support than dynamic ones!Garibold
You can override the GetDynamicMemberNames() method for a dynamic object to return the list names of dynamic members. The problem is that it's not guaranteed that every dynamic object has this method (ExpandoObject doesn't). It's not surprising that reflection works better for static types. It was created for them in the first place. With dynamics, you have to rely more on unit-testing and TDD.Accordion
The MSDN Documentation for GetDynamicMemberNames() mentions: "This method exists for debugging purposes only.", not really comforting :(Warmedover
C
34

If the IDynamicMetaObjectProvider can provide the dynamic member names, you can get them. See GetMemberNames implementation in the apache licensed PCL library Dynamitey (which can be found in nuget), it works for ExpandoObjects and DynamicObjects that implement GetDynamicMemberNames and any other IDynamicMetaObjectProvider who provides a meta object with an implementation of GetDynamicMemberNames without custom testing beyond is IDynamicMetaObjectProvider.

After getting the member names it's a little more work to get the value the right way, but Impromptu does this but it's harder to point to just the interesting bits and have it make sense. Here's the documentation and it is equal or faster than reflection, however, unlikely to be faster than a dictionary lookup for expando, but it works for any object, expando, dynamic or original - you name it.

Countrywide answered 26/10, 2011 at 1:1 Comment(3)
Thanks to get to the point the code is: var tTarget = target as IDynamicMetaObjectProvider; if (tTarget !=null) { tList.AddRange(tTarget.GetMetaObject(Expression.Constant(tTarget)).GetDynamicMemberNames()); }Garibold
thanks for your great library. I was having issues round-tripping a dynamic object to this library dynamicjson.codeplex.com, and was able to extended it with your library.Karissakarita
example please.Agglutinin
A
25

Requires Newtonsoft Json.Net

A little late, but I came up with this. It gives you just the keys and then you can use those on the dynamic:

public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
    JObject attributesAsJObject = dynamicToGetPropertiesFor;
    Dictionary<string, object> values = attributesAsJObject.ToObject<Dictionary<string, object>>();
    List<string> toReturn = new List<string>();
    foreach (string key in values.Keys)
    {
        toReturn.Add(key);                
    }
    return toReturn;
}

Then you simply foreach like this:

foreach(string propertyName in GetPropertyKeysForDynamic(dynamicToGetPropertiesFor))
{
    dynamic/object/string propertyValue = dynamicToGetPropertiesFor[propertyName];
    // And
    dynamicToGetPropertiesFor[propertyName] = "Your Value"; // Or an object value
}

Choosing to get the value as a string or some other object, or do another dynamic and use the lookup again.

Antiproton answered 23/8, 2016 at 3:14 Comment(3)
You dont need the list toReturn.Gunthar
Perfect! I had to change JObject attributesAsJObject = dynamicToGetPropertiesFor; to Object attributesAsJObject = (JObject)JToken.FromObject(obj); though!!Translucid
Just to reiterate what @Translucid said. You need to replace JObject attributesAsJObject = dynamicToGetPropertiesFor; with something like: var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);. At that point, you can get a dictionary of property names and values by doing something like var objProperties = jObject.ToObject<Dictionary<string, object>>();. With that in hand, you are off to the races. This doesn't need a dynamic. It works fine with anything that is a subclass of DynamicObjectJessie
P
1

Simple solution to only reflect over json objects:

using System.Collections.Generic;
using System.Text.Json;
dynamic d = new {a = 1, b = 2, c = 3};
foreach ((string k, object o) in JsonSerializer.Deserialize<Dictionary<string, object>>(JsonSerializer.Serialize(d)))
{

}
Patchwork answered 29/8, 2021 at 20:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.