JSON.NET comes with property attributes like [JsonIgnore]
and [JsonProperty]
.
I want to create some custom ones that get run when the seria
You can write a custom contract resolver like this
public class MyContractResolver<T> : Newtonsoft.Json.Serialization.DefaultContractResolver
where T : Attribute
{
Type _AttributeToIgnore = null;
public MyContractResolver()
{
_AttributeToIgnore = typeof(T);
}
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
var list = type.GetProperties()
.Where(x => !x.GetCustomAttributes().Any(a => a.GetType() == _AttributeToIgnore))
.Select(p => new JsonProperty()
{
PropertyName = p.Name,
PropertyType = p.PropertyType,
Readable = true,
Writable = true,
ValueProvider = base.CreateMemberValueProvider(p)
}).ToList();
return list;
}
}
You can use it in serialization/deserialization like
var json = JsonConvert.SerializeObject(
obj,
new JsonSerializerSettings() {
ContractResolver = new MyContractResolver<JsonIgnoreSerialize>()
});
var obj = JsonConvert.DeserializeObject<SomeType>(
json,
new JsonSerializerSettings() {
ContractResolver = new MyContractResolver<JsonIgnoreDeserialize>()
});
Since your goal is to ignore a property on serialization but not deserialization, you can use a ContractResolver
.
Note that the following class does just that, and is based on CamelCasePropertyNamesContractResolver
, to make sure it serializes to camel-cased Json fields. If you don't want that, you can make it inherit from DefaultContractResolver
instead.
Also, the example I had myself is based on the name of a string, but you can easily check if the property is decorated by your custom attribute instead of comparing the property name.
public class CamelCaseIgnoringPropertyJsonResolver<T> : CamelCasePropertyNamesContractResolver
{
protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
{
// list the properties to ignore
var propertiesToIgnore = type.GetProperties()
.Where(x => x.GetCustomAttributes().OfType<T>().Any());
// Build the properties list
var properties = base.CreateProperties(type, memberSerialization);
// only serialize properties that are not ignored
properties = properties
.Where(p => propertiesToIgnore.All(info => info.Name != p.UnderlyingName))
.ToList();
return properties;
}
}
Then, you can use it as follows:
static private string SerializeMyObject(object myObject)
{
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCaseIgnoringPropertyJsonResolver<JsonIgnoreSerializeAttribute>()
};
var json = JsonConvert.SerializeObject(myObject, settings);
return json;
}
Finally, the custom attribute can be of any type, but to match the example:
internal class JsonIgnoreSerializeAttribute : Attribute
{
}
The approach is tested, and also works with nested objects.
Not sure if this is new, but I would suggest using the method GetSerializableMembers
which handles the member infos directly. This way, one can avoid having to deal with the JsonProperty.
public class MyJsonContractResolver : DefaultContractResolver
{
protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
return base.GetSerializableMembers(objectType)
.Where(mi => mi.GetCustomAttribute<JsonIgnoreSerializeAttribute>() != null)
.ToList();
}
}