Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overriding a property value in custom JSON.net contract resolver

Tags:

json

c#

json.net

I am attempting to implement a custom JSON.net IContractResolver that will replace all null property values with a specified string. I'm aware that this functionality is available via attributes on member of types that get serialized; this is an alternative route that we're considering.

My resolver implementation so far is as follows. StringValueProvider is a simple implementation of IValueProvider that doesn't affect the problem, which is that I can't figure out how to get the value of property as I have no knowledge in this method of the instance that supplied member so I can't pass it in as an argument to GetValue() (marked as WHAT-GOES-HERE? in the code sample).

Is there a way that I can get what I need from member or from property?

public class NullSubstitutionPropertyValueResolver : DefaultContractResolver
{
    private readonly string _substitutionValue;

    public NullSubstitutionPropertyValueResolver(string substitutionValue)
    {
        _substitutionValue = substitutionValue;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty result = base.CreateProperty(member, memberSerialization);

        PropertyInfo property = member as PropertyInfo;

        if (property == null)
        {
            return result;
        }

        // What do I use here to get the property value?
        bool isNull = property.GetValue(WHAT-GOES-HERE?) == null;

        if (isNull)
        {
            result.ValueProvider = new StringValueProvider(_substitutionValue);
        }

        return result;
    }
}
like image 858
Jason Avatar asked Oct 27 '17 14:10

Jason


1 Answers

The contract resolver is not concerned with instances, it is concerned with types. The value provider is concerned with instances. In the contract resolver, you decide whether the value provider should be applied to the property based on the property type (for example, maybe you only want to use a StringValueProvider on string properties?) Then, you make the value provider store a reference to the property (pass it in the constructor along with the substitution value). In the value provider, you can read the value from the object instance, check if it is null and do the appropriate value substitution.

The code should look something like this:

public class NullSubstitutionPropertyValueResolver : DefaultContractResolver
{
    private readonly string _substitutionValue;

    public NullSubstitutionPropertyValueResolver(string substitutionValue)
    {
        _substitutionValue = substitutionValue;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty result = base.CreateProperty(member, memberSerialization);

        PropertyInfo property = member as PropertyInfo;

        if (property.PropertyType == typeof(string))
        {
            result.ValueProvider = new StringValueProvider(property, _substitutionValue);
        }

        return result;
    }
}

public class StringValueProvider : IValueProvider
{
    private PropertyInfo _targetProperty;
    private string _substitutionValue;

    public StringValueProvider(PropertyInfo targetProperty, string substitutionValue)
    {
        _targetProperty = targetProperty;
        _substitutionValue = substitutionValue;
    }

    // SetValue gets called by Json.Net during deserialization.
    // The value parameter has the original value read from the JSON;
    // target is the object on which to set the value.
    public void SetValue(object target, object value)
    {
        _targetProperty.SetValue(target, value);
    }

    // GetValue is called by Json.Net during serialization.
    // The target parameter has the object from which to read the value;
    // the return value is what gets written to the JSON
    public object GetValue(object target)
    {
        object value = _targetProperty.GetValue(target);
        return value == null ? _substitutionValue : value;
    }
}

Here is a working demo: https://dotnetfiddle.net/PAZULK

like image 180
Brian Rogers Avatar answered Sep 28 '22 07:09

Brian Rogers