web-dev-qa-db-fra.com

Existe-t-il un moyen d'ignorer les propriétés get-only dans Json.NET sans utiliser les attributs JsonIgnore?

Existe-t-il un moyen d'ignorer les propriétés get-only en utilisant le sérialiseur Json.NET mais sans utiliser les attributs JsonIgnore?

Par exemple, j'ai une classe avec ces propriétés 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;
        }
    }

Dois-je ajouter [JsonIgnore] à tous (j'ai aussi beaucoup d'autres classes), ou il y a une meilleure façon d'ignorer toutes les propriétés get-only?

40
Jaex

Vous pouvez le faire en implémentant un IContractResolver personnalisé et en l'utilisant pendant la sérialisation. Si vous sous-classe le DefaultContractResolver, cela devient très facile à faire:

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

Voici un programme de test montrant comment l'utiliser:

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

Voici la sortie de ce qui précède. Notez que la propriété en lecture seule LowerCaseName n'est pas incluse dans la sortie.

{"Id":2,"Name":"Joe Schmoe"}
66
Brian Rogers

Utilisez le mode OptIn de JSON.net et vous n'aurez qu'à décorer les propriétés que vous souhaitez sérialiser. Ce n'est pas aussi bon que de désactiver automatiquement toutes les propriétés en lecture seule, mais cela peut vous faire économiser du travail.

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

    public string nonSerializedProp { get; set; }
}

date: Ajout d'une autre possibilité en utilisant la réflexion

Si la solution ci-dessus n'est toujours pas tout à fait ce que vous recherchez, vous pouvez utiliser la réflexion pour créer des objets de dictionnaire qui seront ensuite sérialisés. Bien sûr, l'exemple ci-dessous ne fonctionnera que pour les classes simples, vous devrez donc ajouter la récursivité si vos classes contiennent d'autres classes. Cela devrait au moins vous orienter dans la bonne direction.

Le sous-programme pour mettre le résultat filtré dans un dictionnaire:

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

Un extrait montrant son utilisation:

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

Vous pouvez utiliser un résolveur de contrat comme celui-ci:

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

Il exclura les propriétés calculées, mais inclura C # 6 pour obtenir uniquement les propriétés et toutes les propriétés avec une méthode set.

9
Johan Larsson

Json.net a la capacité de sérialiser conditionnellement des propriétés sans attribut ni résolveur de contrat. Ceci est particulièrement utile si vous ne voulez pas que votre projet dépende de Json.net.

Selon la documentation Json.net

Pour sérialiser conditionnellement une propriété, ajoutez une méthode qui renvoie un booléen portant le même nom que la propriété, puis préfixez le nom de la méthode avec ShouldSerialize. Le résultat de la méthode détermine si la propriété est sérialisée. Si la méthode renvoie true, la propriété sera sérialisée, si elle retourne false, la propriété sera ignorée.

7
Mark