Reputation: 946
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
}
Upvotes: 8
Views: 7401
Reputation: 129777
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:
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; }
}
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();
Upvotes: 5
Reputation: 946
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);
}
Upvotes: 0