Fundamentally, I want to include or omit a property from the generated Json based on its value at the time of serialization.
More-specifically, I have a type that knows if a value has been assigned to it and I only want to serialize properties of that type if there has been something assigned to it (so I need to inspect the value at runtime). I’m trying to make it easy for my API to detect the difference between “has the default value” and “wasn’t specified at all”.
A custom JsonConverter does not seem sufficient; I tried it and I believe the property name is already serialized before the converter is called. In my case I want to omit even the property name.
I’ve looked at extending DefaultContractResolver but CreateProperty and CreateProperties (which return JsonProperty serialization metadata) take only the Type being serialized, so I can’t inspect the instance itself. In general, I don’t see anything on the DefaultContractResolver allowing me to control if an instance is serialized; maybe I missed it.
I also thought maybe I needed to create a ContractResolver that returned a custom JsonObjectContract for my type. But, again, I don’t see anything on JsonObjectContract that makes decisions based on an instance.
Is there a good way to accomplish my goal? Am I just missing something simple? Any help you can provide is greatly appreciated. Since Json.NET is so extensible, I thought this wouldn’t be too hard. But I’m starting to think I’m way off in the weeds here. 🙂
Ok, after digging around in Json.NET source for a while, I finally got this working and it will even honor the ShouldSerialize* and *Specified members that Json.NET supports. Be warned: this is definitely going off into the weeds.
So I realized that the JsonProperty class returned by DefaultContractResolver.CreateProperty has ShouldSerialize and Converter properties, which allow me to specify if the property instance should actually be serialized and, if so, how to do it.
Deserialization requires something a little different, though. DefaultContractResolver.ResolveContract will, by default for a custom type, return a JsonObjectContract with a null Converter property. In order to deserialize my type properly, I needed to set the Converter property when the contract is for my type.
Here’s the code (with error handling / etc removed to keep things as small as possible).
First, the type that needs special handling:
And there’s the converter that will serialize it properly after we know it should be serialized:
Finally, and most-verbosely, here’s the ContractResolver that inserts the hooks:
I hope that helps somebody else. Feel free to ask questions if anything is unclear or if it looks like I missed something.