Is there a way to ignore get-only properties in Json.NET without using JsonIgnore attributes?

C#Jsonjson.net

C# Problem Overview


Is there a way to ignore get-only properties using the Json.NET serializer but without using JsonIgnore attributes?

For example, I have a class with these get properties:

    public Keys Hotkey { get; set; }

    public Keys KeyCode
    {
        get
        {
            return Hotkey & Keys.KeyCode;
        }
    }

    public Keys ModifiersKeys
    {
        get
        {
            return Hotkey & Keys.Modifiers;
        }
    }

    public bool Control
    {
        get
        {
            return (Hotkey & Keys.Control) == Keys.Control;
        }
    }

    public bool Shift
    {
        get
        {
            return (Hotkey & Keys.Shift) == Keys.Shift;
        }
    }

    public bool Alt
    {
        get
        {
            return (Hotkey & Keys.Alt) == Keys.Alt;
        }
    }

    public Modifiers ModifiersEnum
    {
        get
        {
            Modifiers modifiers = Modifiers.None;

            if (Alt) modifiers |= Modifiers.Alt;
            if (Control) modifiers |= Modifiers.Control;
            if (Shift) modifiers |= Modifiers.Shift;

            return modifiers;
        }
    }

    public bool IsOnlyModifiers
    {
        get
        {
            return KeyCode == Keys.ControlKey || KeyCode == Keys.ShiftKey || KeyCode == Keys.Menu;
        }
    }

    public bool IsValidKey
    {
        get
        {
            return KeyCode != Keys.None && !IsOnlyModifiers;
        }
    }

Do I need to add [JsonIgnore] to all of them (I also have many other classes), or there is better way to ignore all get-only properties?

C# Solutions


Solution 1 - C#

You can do this by implementing a custom IContractResolver and using that during serialization. If you subclass the DefaultContractResolver, this becomes very easy to do:

class WritablePropertiesOnlyResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        IList<JsonProperty> props = base.CreateProperties(type, memberSerialization);
        return props.Where(p => p.Writable).ToList();
    }
}

Here is a test program demonstrating how to use it:

using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

class Program
{
    static void Main(string[] args)
    {
        Widget w = new Widget { Id = 2, Name = "Joe Schmoe" };

        JsonSerializerSettings settings = new JsonSerializerSettings
        {
            ContractResolver = new WritablePropertiesOnlyResolver()
        };

        string json = JsonConvert.SerializeObject(w, settings);

        Console.WriteLine(json);
    }
}

class Widget
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string LowerCaseName
    {
        get { return (Name != null ? Name.ToLower() : null); }
    }
}

Here is the output of the above. Notice that the read-only property LowerCaseName is not included in the output.

{"Id":2,"Name":"Joe Schmoe"}

Solution 2 - C#

Use the OptIn mode of JSON.net and you'll only need to decorate the properties you want to serialize. This isn't as good as automatically opting out all read only properties, but it can save you some work.

[JsonObject(MemberSerialization.OptIn)]
public class MyClass
{
    [JsonProperty]
    public string serializedProp { get; set; }
    
    public string nonSerializedProp { get; set; }
}

Udate: Added another possibility using reflection

If the above solution still isn't quite what you're looking for, you could use reflection to make dictionary objects which would then be serialized. Of course the example below will only work for simple classes, so you would need to add recursion if your classes contain other classes. This should at least point you in the right direction.

The subroutine to put the filtered result into a dictionary:

    private Dictionary<String, object> ConvertToDictionary(object classToSerialize)
    {
        Dictionary<String, object> resultDictionary = new Dictionary<string, object>();

        foreach (var propertyInfo in classToSerialize.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
        {
            if (propertyInfo.CanWrite) resultDictionary.Add(propertyInfo.Name, propertyInfo.GetValue(classToSerialize, null));
        }

        return resultDictionary;
    }

A snippet showing its use:

SampleClass sampleClass = new SampleClass();
sampleClass.Hotkey = Keys.A;
var toSerialize = ConvertToDictionary(sampleClass);
String resultText = JsonConvert.SerializeObject(toSerialize);

Solution 3 - C#

You can use a contract resolver like this:

public class ExcludeCalculatedResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        property.ShouldSerialize = _ => ShouldSerialize(member);
        return property;
    }

    internal static bool ShouldSerialize(MemberInfo memberInfo)
    {
        var propertyInfo = memberInfo as PropertyInfo;
        if (propertyInfo == null)
        {
            return false;
        }

        if (propertyInfo.SetMethod != null)
        {
            return true;
        }

        var getMethod = propertyInfo.GetMethod;
        return Attribute.GetCustomAttribute(getMethod, typeof(CompilerGeneratedAttribute)) != null;
    }
}

It will exclude calculated properties but include C#6 get only properties and all properties with a set method.

Solution 4 - C#

Json.net does have the ability to conditionally serialize properties without an attribute or contract resolver. This is especially useful if you don't want your project to have a dependency on Json.net.

> As per the Json.net documentation > >To conditionally serialize a property, add a method that returns boolean with > the same name as the property and then prefix the method name with > ShouldSerialize. The result of the method determines whether the > property is serialized. If the method returns true then the property > will be serialized, if it returns false then the property will be > skipped. >

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJaexView Question on Stackoverflow
Solution 1 - C#Brian RogersView Answer on Stackoverflow
Solution 2 - C#Lathejockey81View Answer on Stackoverflow
Solution 3 - C#Johan LarssonView Answer on Stackoverflow
Solution 4 - C#MarkView Answer on Stackoverflow