12

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.

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
David Brabant
  • 41,623
  • 16
  • 83
  • 111
  • It doesn't say anything about `ShouldDeserialize` in the [documentation](http://www.newtonsoft.com/json/help/html/ConditionalProperties.htm). Why would you want to serialize something but not deserialize it? – kjbartel Feb 19 '16 at 10:22
  • @kjbartel Same model for serialization / deserialization but when deserializing there is an intermediarly link (hateoas) for accessing that specific property. – David Brabant Feb 19 '16 at 11:00
  • 2
    See also [Serialize Property, but Do Not Deserialize Property in Json.Net](http://stackoverflow.com/q/31731320/10263) – Brian Rogers Feb 19 '16 at 21:33
  • @kjbartel: "Why would you want to serialize something but not deserialize it?" - in my case (that led me to finding this question): Because the backend needs to return some additional information that will be displayed in the web-based UI frontend, which cannot be changed by the UI frontend, anyway. Add to that, the fact that this unchangeable information is in an object typed as an interface makes deserialization a hassle. – O. R. Mapper Dec 16 '21 at 10:05

1 Answers1

17

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.

dbc
  • 104,963
  • 20
  • 228
  • 340
  • This is almost the same as your [other answer](http://stackoverflow.com/a/31732029/1730559) on the duplicate question. Why didn't you just flag this question as a duplicate? – kjbartel Feb 22 '16 at 09:35
  • Thanks a lot for your solution, but for some reason the object `o` in the predicate is null and thus, `property.ShouldDeserialize` is not assigned and gives an error. Could you please suggest what could be a source of null for `o`? – Ivan B Dec 27 '19 at 08:37
  • @IvanB - can you share a [mcve]? – dbc Dec 28 '19 at 23:43