Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add a custom attribute to json.net

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?

like image 860
Carl Thomas Avatar asked Oct 02 '15 13:10

Carl Thomas


People also ask

Does JSON have attributes?

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.

What is Jsonproperty attribute in C#?

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.


2 Answers

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>()
            });
like image 177
Eser Avatar answered Oct 23 '22 12:10

Eser


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.

like image 23
Fabio Salvalai Avatar answered Oct 23 '22 10:10

Fabio Salvalai