JSON.NET comes with property attributes like [JsonIgnore]
and [JsonProperty]
.
I want to create some custom ones that get run when the serialisation runs e.g.
[JsonIgnoreSerialize]
or [JsonIgnoreDeserialize]
How would I go about extending the framework to include this?
JSON does not have a similar notion to attributes as in XML. Because of this, attributes must be represented in JSON in a different manner. Here, we see that JSON properties just contain objects or values - no attributes. NIEM attributes are represented as regular properties.
JsonPropertyAttribute indicates that a property should be serialized when member serialization is set to opt-in. It includes non-public properties in serialization and deserialization. It can be used to customize type name, reference, null, and default value handling for the property value.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With