Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional serialization/deserialization of object properties with json.net [duplicate]

Tags:

c#

json.net

I have a scenario with a class defined as below:

class MyObject
{
    public DataDictionary MyObjectData { get; set; }

    public bool ShouldSerializeMyObjectData() { return true; }
    public bool ShouldDeserializeMyObjectData() { return false; }
}

When I attempt to serialize/deserialize that class with JSON.net, it takes the ShouldSerialize into account, but not the ShouldDeserialize.

According to the documentation, both should work the same way I guess. Is there something particular I should know? More generally, how should I deal with scenarios where I want to serialize a property but not deserialize it?

I'm using Json.NET 8.0 if that matters.

Thanks for your help.

like image 940
David Brabant Avatar asked Feb 19 '16 09:02

David Brabant


Video Answer


1 Answers

The short answer to your question is, automatically checking for ShouldDeserialize{PropertyName}() is not currently implemented even though ShouldSerialize{PropertyName}() is. A longer answer and workaround follow.

The class JsonProperty is used internally by Json.NET to define a contract for how to map a JSON property to a .NET member or constructor parameter. It has two predicate properties, ShouldSerialize and ShouldDeserialize that, when non-null, prevent a property from being serialized and deserialized, respectively. Initializing each JsonProperty is the job of the ContractResolver. For each property {PropertyName}, Json.NET's default contract resolver automatically checks for the presence of a public bool ShouldSerialize{PropertyName}() method. If such a method exists, it adds a call to it in the ShouldSerialize predicate, thereby suppressing serialization when the method returns false. This was implemented because controlling property serialization via a method ShouldSerialize{PropertyName}() is a standard pattern supported by, e.g., XmlSerializer. For more background see the relevant Json.NET release notes.

For example, in the following class, serialization of MyObjectData will be suppressed unless MyObjectData.Count > 0:

class MyObject
{
    public DataDictionary MyObjectData { get; set; }

    public bool ShouldSerializeMyObjectData() { return MyObjectData != null && MyObjectData.Count > 0; }
}

JsonProperty.ShouldDeserialize, however, it is never set by the default contract resolver. This may be due to the fact that there is no standard pattern for deserialization equivalent to ShouldSerialize{PropertyName}() and so Newtonsoft never had any requests to implement such a pattern. Nevertheless, as you have noticed, infrastructure to support such a pattern exists, and so applications can create custom contract resolvers that do just that. In fact, Json.NET has an example of such a contract resolver in its own test suite:

public class ShouldDeserializeContractResolver : DefaultContractResolver
{
    public static new readonly ShouldDeserializeContractResolver Instance = new ShouldDeserializeContractResolver();

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

        MethodInfo shouldDeserializeMethodInfo = member.DeclaringType.GetMethod("ShouldDeserialize" + member.Name);

        if (shouldDeserializeMethodInfo != null)
        {
            property.ShouldDeserialize = o => { return (bool)shouldDeserializeMethodInfo.Invoke(o, null); };
        }

        return property;
    }
}

public class ShouldDeserializeTestClass
{
    [JsonExtensionData]
    public IDictionary<string, JToken> ExtensionData { get; set; }

    public bool HasName { get; set; }
    public string Name { get; set; }

    public bool ShouldDeserializeName()
    {
        return HasName;
    }
}

If you want to conditionally suppress deserialization of properties even when present in the JSON, you may use this contract resolver.

Notes:

  • If you do use a custom contract resolver, you should cache and reuse it for best performance.

  • JsonProperty.ShouldDeserialize is called before the property value is deserialized. If it returns true, the property is skipped, with no ability to examine the contents of the property. Thus it cannot be used to implement custom filtering based on that value.

  • A JSON object is defined by the JSON standard as an unordered set of name/value pairs. Thus a ShouldDeserialize method that assumes that other properties have already been read in may be brittle.

    Instead, if you want to skip deserialization of one property based on the value of another, consider using an [OnDeserialized] callback and clearing the unwanted value there, after all properties have been deserialized.

like image 136
dbc Avatar answered Oct 01 '22 18:10

dbc