Search code examples
json.netedgejs

Ignore ExpandoObject Properties with JSON.Net Serializer Settings


Is it possible to ignore ExpandoObject properties, in particular those of Delegate type, when using JsonConvert(expando, formatting, serializerSettings)?

Essentially I'm trying to ignore all parsing of the func property in this example expando object:

//{
//  func: () => {}
//}
Action func = () => {};
dynamic expando = new ExpandoObject();
expando.func = func;

// should be empty object {}
string json = JsonConvert(expando, formatting, serializerSettings);

The first thing I tried was overriding the converter. Unfortunately this doesn't work, as I see CanConvert called recursively for Action -> DelegateEntry -> some generic type -> RuntimeMethodInfo.

    private class ExpandoObjectIgnoreConverter : ExpandoObjectConverter
    {
        public override bool CanConvert(Type objectType)
        {
            if (typeof(Delegate).IsAssignableFrom(objectType))
            {
                return false;
            }
            return base.CanConvert(objectType);
        }
    }

A method that works is using an error handler in serialization settings and a contract resolver. When I throw the error, all further processing of the property is ignored, i.e. Action -> DelegateEntry -> some generic type -> RuntimeMethodInfo. However, I'd like to do this more elegantly than throwing an exception if possible.

Error handler:

        serializationSettings.Error = (sender, args) =>
        {
            if (args.ErrorContext.Error is InvalidCastException)
            {
                args.ErrorContext.Handled = true;
            }
        }

Contract resolver:

    private class ExpandoObjectContractResolver : DefaultContractResolver
    {
        public override JsonContract ResolveContract(Type type)
        {
            if (typeof(Delegate).IsAssignableFrom(type))
            {
                throw new InvalidCastException();
            }
            else
            {
                return base.ResolveContract(type);
            }
        }
    }

I'm using the edge library to script nodejs from within a C# process. I'm trying to remove functions from the returned javascript objects from within C#, as they are assigned a Delegate type that doesn't play nicely with JsonConvert.


Solution

  • ExpandoObjectConverter does not have any custom code to write an ExpandoObject. Instead it overrides JsonConverter.CanWrite to return false thereby allowing the expando to be serialized generically as an IDictionary<string, object>.

    Thus you can override CanWrite and WriteJson() yourself to filter undesired key/value pairs before serialization:

    public class FilteredExpandoObjectConverter : ExpandoObjectConverter
    {
        public override bool CanWrite { get { return true; } }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            var expando = (IDictionary<string, object>)value;
            var dictionary = expando
                .Where(p => !(p.Value is System.Delegate))
                .ToDictionary(p => p.Key, p => p.Value);
            serializer.Serialize(writer, dictionary);
        }
    }
    

    Then use the converter in settings as follows:

    var formatting = Formatting.Indented;
    var serializerSettings = new JsonSerializerSettings
    {
        Converters = { new FilteredExpandoObjectConverter() },
    };
    var json = JsonConvert.SerializeObject(expando, formatting, serializerSettings);
    

    Note this will only filter delegates values directly owned by an ExpandoObject. If you have a collection containing some delegates, or delegate-valued members in some POCO, they will not be filtered by this code.

    Sample fiddle.