12

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?

2
  • do they need to be attributes ? what do you want to do in your custom serialization attribute ? Commented Oct 2, 2015 at 13:48
  • I would like to be able to ignore a property on serialisation but not on deserialisation. I thought it would be easy to just add an attribute e.g. [JsonIgnoreSerialize] to the property. Commented Oct 2, 2015 at 13:50

3 Answers 3

13

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>()
            });
Sign up to request clarification or add additional context in comments.

2 Comments

This solution works as advertised, but it has a problem: it does not honor the standard Json.NET attributes [JsonIgnore] and (more importantly) [JsonProperty]. See dotnetfiddle.net/Et265h. You should be calling base.CreateProperties() first, then filtering that list based on the custom attributes. See dotnetfiddle.net/gDGfrO.
I more prefer to override JsonPropertyAttribute's deserialization and serialization behavior than adding ContractResolver and call serialization method. Does someone know how it could achieve?
4

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.

6 Comments

I did not downvote, but I would guess the reason is that your solution does not actually show how to implement the custom attributes as was originally asked. Instead, one has to pass the name of the property to ignore to your resolver and it only handles that single property. What if there are more than one?
Fair enough, Brian. Thank you for the constructive comment :)
I have updated my answer with a more suitable implementation.
Looks better. One question: what is TrackExtended in the signature of your SerializeMyObject method? Shouldn't that just be object?
Absolutely. Sorry, that was a leftover of my sample to test the code ;) -Fixed!
|
0

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();
  }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.