Есть ли способ игнорировать свойства get-only в Json.NET без использования атрибутов JsonIgnore?

Есть ли способ игнорировать свойства get-only с помощью сериализатора Json.NET, но без использования атрибутов JsonIgnore?

Например, у меня есть класс с этими свойствами get:

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

Нужно ли добавлять [JsonIgnore] ко всем из них (у меня также много других классов), или есть лучший способ игнорировать все свойства get-only?

Ответ 1

Вы можете сделать это, выполнив пользовательский IContractResolver и используя это во время сериализации. Если вы подклассифицируете DefaultContractResolver, это будет очень легко сделать:

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

Вот тестовая программа, демонстрирующая, как ее использовать:

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

Вот результат вышесказанного. Обратите внимание, что свойство read-only LowerCaseName не включено в вывод.

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

Ответ 2

Используйте режим OptIn для JSON.net, и вам нужно будет только украсить свойства, которые вы хотите сериализовать. Это не так хорошо, как автоматическое отключение всех свойств только для чтения, но это может сэкономить вам некоторую работу.

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

    public string nonSerializedProp { get; set; }
}

Udate: добавлена ​​другая возможность с использованием отражения

Если приведенное выше решение по-прежнему не совсем то, что вы ищете, вы можете использовать отражение для создания объектов словаря, которые затем будут сериализованы. Конечно, приведенный ниже пример будет работать только для простых классов, поэтому вам нужно будет добавить рекурсию, если ваши классы содержат другие классы. Это должно по крайней мере указывать на вас в правильном направлении.

Подпрограмма, чтобы поместить отфильтрованный результат в словарь:

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

Отрывок, показывающий его использование:

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

Ответ 3

Вы можете использовать распознаватель контракта следующим образом:

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

Он исключает вычисленные свойства, но включает С# 6, получая только свойства и все свойства с заданным методом.

Ответ 4

Json.net имеет возможность условно сериализовать свойства без атрибута или разрешения контракта. Это особенно полезно, если вы не хотите, чтобы ваш проект зависел от Json.net.

В соответствии с Документация Json.net

Чтобы условно сериализовать свойство, добавьте метод, который возвращает boolean с то же имя, что и свойство, а затем префикс имени метода ShouldSerialize. Результат метода определяет, свойство сериализуется. Если метод возвращает true, свойство будет сериализован, если он вернет false, тогда свойство будет пропущен.