8

I have a class that is annotated with DataContract and DataMember attributes. Some members are marked as DataMember(IsRequired = true). When I serialize instances over the wire from Json.NET, and the required object members have null value, then their serialized values are missing in the output (which is apparently equivalent to being null in JSON). I'm okay with that.

I've created a sort of "echo" service which returns data sent to it as a response. So this service receives the JSON with missing members (or null members depending on how you look at it), and then sends it right back to my Json.NET client. The JSON on the wire looks the same in both directions as viewed through Fiddler (a proxy sniffer). So far so good.

When the original Json.NET sender receives the JSON response to deserialize it, the serializer throws an exception about not finding required members in the JSON payload:

Required property 'IAmRequired' not found in JSON. Path ''.

That is unfortunate, as the serializer is thus not able to deserialize data that it had previously serialized without a problem.

Short of changing the DataContract class to make the member not required (which I do not want to do for a number of reasons), is there a way to make Json.NET deserialize missing members to default values such as null?

Here is my deserialization code:

HasRequired h = null;
JObject json = response as JObject; // hand waving here
try
{
    JsonSerializer ser = new JsonSerializer();
    ser.MissingMemberHandling = MissingMemberHandling.Ignore; // doesn't seem to help
    ser.DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate; // doesn't seem to help
    ser.NullValueHandling = NullValueHandling.Include; // doesn't seem to help
    h = json.ToObject<HasRequired>(ser);
}
catch (Exception ex)
{
    // bummer, missing required members still
}
Greg
  • 906
  • 1
  • 9
  • 22
  • Wait, so you have members marked as required but they really aren't required? – Brian Rogers Dec 18 '14 at 13:24
  • They are required... but only to satisfy some obtuse rules for DataContractSerializer to be able to handle the serialization for WCF (for a SOAP binding), and not have the types look horrible to clients. I know what you are saying; if not for the DC serializer, they would be optional. For the SOAP serialization end, which I tried to avoid mentioning, the objects are serialized as XML null instances (they are explicitly sent as null). – Greg Dec 18 '14 at 13:47
  • I understand. I've added an answer that should help you. – Brian Rogers Dec 20 '14 at 21:17

2 Answers2

5

If you have properties marked with [DataMember(Required = true)] and you want to override the required behavior, there are a couple of things you can do:

  1. You can mark those same properties with [JsonProperty(Required = Required.Default)]. This works because [JsonProperty] takes precedence over [DataMember] in Json.Net.

    [DataContract]
    public class HasRequired
    {
        [DataMember(Required = true)]
        [JsonProperty(Required = Required.Default)]
        public string IAmRequired { get; set; }
    }
    
  2. Or, you can create a custom ContractResolver that programmatically sets Required = Required.Default on every property.

    class CustomResolver : DefaultContractResolver
    {
        protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
        {
            JsonProperty prop = base.CreateProperty(member, memberSerialization);
            prop.Required = Required.Default;
            return prop;
        }
    }
    

    To use the resolver, simply set the ContractResolver property on the serializer to a new instance of the custom resolver:

    JsonSerializer ser = new JsonSerializer();
    ser = new CustomResolver();
    
Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
0

This looks like a real wart on my code, and needs to be repeated for every nullable-but-required member, but it works, and seems to add negligible overhead. I added this just before the try block in my original snippet:

JToken maybeHasIt = null;
if (!json.TryGetValue("IAmRequired", StringComparison.InvariantCultureIgnoreCase, out maybeHasIt))
{
    json.Add("IAmRequired", null);
}
Greg
  • 906
  • 1
  • 9
  • 22