5

Is there an attribute to tell json.net to ignore all properties of a class but include all fields (regardless of access modifiers) ?

If not is there a way to create one ?

Basically I want one attribute decorating the class that will have the equivalent effect to putting [JsonIgnore] infront of each property.

kofifus
  • 17,260
  • 17
  • 99
  • 173

3 Answers3

9

You can add [JsonObject(MemberSerialization.OptIn)] attribute to your class, then everything will be ignored unless you explicitly Opt-In by using a [JsonProperty] attribute on the members.

[JsonObject(MemberSerialization.OptIn)]
public class Address
{
    [JsonProperty]
    private string _field1 = "bob";

    public string Line1 { get; set; }

    public string Line2 { get; set; }

    public string Line3 { get; set; }
}

For example

using System;
using AutoFixture;
using Newtonsoft.Json;

public class Program
{
    public static void Main()
    {
        var fixture = new Fixture();
        var address = fixture.Create<Address>(); // Create an address filled with junk

        var json = JsonConvert.SerializeObject(address);

        Console.WriteLine(json);
    }
}

Will output:

{"_field1":"bob"}
Kevin Smith
  • 13,746
  • 4
  • 52
  • 77
  • Thanks I am specifically looking for a single attribute I can apply to the class and forget about it if I later add/remove fields etc – kofifus Jul 02 '18 at 23:28
  • But.. The code in the answer from Kevin will do that, @kofifus. It's tagged as opt-in. It's exactly the same as excluding everything but the things you say otherwise. – James Whyte Jul 05 '18 at 02:04
  • imagine I have ten fields, I'll need ten [JsonProperty] with your solution – kofifus Jul 05 '18 at 04:41
2

If you mark your class with [JsonObject(MemberSerialization.Fields)], that will get you most of the way there. This attribute tells Json.Net that you want it to serialize all fields in a class, regardless of access modifiers, but not properties.

However, if you have any auto properties in your class (i.e. those declared with { get; set; }) then this attribute will cause the compiler-generated backing fields to be serialized as well, which you may not want. To suppress those, you will need to use a custom IContractResolver. You can create one by inheriting from the DefaultContractResolver and overriding the CreateProperty method. In that method, check whether the class has the [JsonObject(MemberSerialization.Fields)] attribute applied, and if so, check whether the member is a compiler-generated field. If it is, then set it to be ignored.

class CustomResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty prop = base.CreateProperty(member, memberSerialization);
        JsonObjectAttribute attr = prop.DeclaringType.GetCustomAttribute<JsonObjectAttribute>();
        if (attr != null && attr.MemberSerialization == MemberSerialization.Fields && 
            member.GetCustomAttribute<CompilerGeneratedAttribute>() != null)
        {
            prop.Ignored = true;
        }
        return prop;
    }
}

To use the resolver, you need to pass it to the SerializeObject method via the JsonSerializerSettings:

var settings = new JsonSerializerSettings
{
    ContractResolver = new CustomResolver(),
    Formatting = Formatting.Indented
};
string json = JsonConvert.SerializeObject(yourObject, settings);

Here is a proof of concept: https://dotnetfiddle.net/aNXWbn

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
0

You could create a custom IContractResolver and decide to serialize depending on a custom attribute you created:

public class IgnorePropertyResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);

        //Todo: Check your custom attribute. You have MemberInfo here to navigate to your class and GetCustomAttributes<>
        //e.g: property.ShouldSerialize = member.DeclaringType.GetCustomAttribute<JsonIgnoreAllProperties>() == null;
        property.ShouldSerialize = false; 
        return property;
    }
}

Then you need to register it as default, which depends on your environment. Or you are using it directly, then you even do not need an attribute:

string json =
    JsonConvert.SerializeObject(
        product,
        Formatting.Indented,
        new JsonSerializerSettings
        {
            ContractResolver = new IgnorePropertyResolver()
        }
    );

An attribute is created for example by:

public class JsonIgnoreAllPropertiesAttribute : Attribute
{
}

And Used:

[JsonIgnoreAllProperties]
public class ClassToSerialize
{
     public int Ignored { get;set; }
     public int Serialized;
}
Christian Gollhardt
  • 16,510
  • 17
  • 74
  • 111
  • it's not a 'custom attribute' I want to decorate my class so that _all_ fields are considered and _all_ properties are ignored, just as if I had [JsonIgnore] infront of each property – kofifus Jul 03 '18 at 02:36
  • Yeah, that seems not to be supported. So if you want to: You need to create a custom one `[JsonIgnoreAllProperties]` @kofifus – Christian Gollhardt Jul 03 '18 at 02:37
  • any tips on how to do that ? – kofifus Jul 03 '18 at 02:55