161 votes

Faire en sorte qu'une propriété soit désérialisée mais pas sérialisée avec json.net

Nous avons quelques fichiers de configuration qui ont été générés en sérialisant des objets C# avec Json.net.

Nous aimerions faire passer une propriété de la classe sérialisée d'une simple propriété d'enum à une propriété de classe.

Une façon simple de le faire serait de laisser l'ancienne propriété enum sur la classe, et de faire en sorte que Json.net lise cette propriété lorsque nous chargeons la configuration, mais ne la sauvegarde pas à nouveau lors de la prochaine sérialisation de l'objet. Nous traiterons séparément la génération de la nouvelle classe à partir de l'ancienne propriété enum.

Existe-t-il un moyen simple de marquer (par exemple avec des attributs) une propriété d'un objet C#, de sorte que Json.net l'ignore UNIQUEMENT lors de la sérialisation, mais y prête attention lors de la désérialisation ?

167voto

Brian Rogers Points 12160

Il existe en fait plusieurs approches assez simples que vous pouvez utiliser pour obtenir le résultat que vous souhaitez.

Supposons, par exemple, que vos classes soient actuellement définies comme suit :

class Config
{
    public Fizz ObsoleteSetting { get; set; }
    public Bang ReplacementSetting { get; set; }
}

enum Fizz { Alpha, Beta, Gamma }

class Bang
{
    public string Value { get; set; }
}

Et tu veux faire ça :

string json = @"{ ""ObsoleteSetting"" : ""Gamma"" }";

// deserialize
Config config = JsonConvert.DeserializeObject<Config>(json);

// migrate
config.ReplacementSetting = 
    new Bang { Value = config.ObsoleteSetting.ToString() };

// serialize
json = JsonConvert.SerializeObject(config);
Console.WriteLine(json);

Pour obtenir ça :

{"ReplacementSetting":{"Value":"Gamma"}}

Approche 1 : ajouter une méthode ShouldSerialize

Json.NET a la capacité de sérialiser les propriétés de manière conditionnelle en recherchant les propriétés correspondantes. ShouldSerialize dans la classe.

Pour utiliser cette fonctionnalité, ajoutez un booléen ShouldSerializeBlah() à votre classe où Blah est remplacé par le nom de la propriété que vous ne voulez pas sérialiser. Faites en sorte que l'implémentation de cette méthode renvoie toujours false .

class Config
{
    public Fizz ObsoleteSetting { get; set; }

    public Bang ReplacementSetting { get; set; }

    public bool ShouldSerializeObsoleteSetting()
    {
        return false;
    }
}

Remarque : si vous aimez cette approche mais que vous ne voulez pas brouiller l'interface publique de votre classe en introduisant une fonction ShouldSerialize vous pouvez utiliser une méthode IContractResolver pour faire la même chose de manière programmatique. Voir Sérialisation conditionnelle des propriétés dans la documentation.

Approche 2 : Manipuler le JSON avec des JObjects

Au lieu d'utiliser JsonConvert.SerializeObject pour effectuer la sérialisation, charger l'objet config dans un fichier JObject il suffit alors de supprimer la propriété indésirable du JSON avant de l'écrire. Il ne s'agit que de quelques lignes de code supplémentaires.

JObject jo = JObject.FromObject(config);

// remove the "ObsoleteSetting" JProperty from its parent
jo["ObsoleteSetting"].Parent.Remove();

json = jo.ToString();

Approche 3 : (ab)utilisation intelligente des attributs

  1. Appliquer un [JsonIgnore] à la propriété que vous ne voulez pas sérialiser.
  2. Ajoutez un suppléant, privé à la classe avec le même type que la propriété d'origine. Faites en sorte que l'implémentation de cette propriété définisse la propriété originale.
  3. Appliquer un [JsonProperty] à la propriété alternative, en lui donnant le même nom JSON que la propriété originale.

Voici la version révisée Config classe :

class Config
{
    [JsonIgnore]
    public Fizz ObsoleteSetting { get; set; }

    [JsonProperty("ObsoleteSetting")]
    private Fizz ObsoleteSettingAlternateSetter
    {
        // get is intentionally omitted here
        set { ObsoleteSetting = value; }
    }

    public Bang ReplacementSetting { get; set; }
}

61voto

Iucounu Points 301

Pour toutes les situations où il est acceptable que votre propriété réservée à la désérialisation soit marquée comme interne, il existe une solution remarquablement simple qui ne dépend pas du tout des attributs. Il suffit de marquer la propriété comme internal get, mais public set :

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

Il en résulte une désérialisation correcte en utilisant les paramètres/résolveurs/etc. par défaut, mais la propriété est supprimée de la sortie sérialisée.

40voto

Jraco11 Points 3363

Je préfère m'en tenir aux attributs sur ce point, voici la méthode que j'utilise lorsqu'il faut désérialiser une propriété mais pas la sérialiser ou vice versa.

ÉTAPE 1 - Créer l'attribut personnalisé

public class JsonIgnoreSerializationAttribute : Attribute { }

ÉTAPE 2 - Créer un resoudeur de contrat personnalisé

class JsonPropertiesResolver : DefaultContractResolver
{
    protected override List<MemberInfo> GetSerializableMembers(Type objectType)
    {
        //Return properties that do NOT have the JsonIgnoreSerializationAttribute
        return objectType.GetProperties()
                         .Where(pi => !Attribute.IsDefined(pi, typeof(JsonIgnoreSerializationAttribute)))
                         .ToList<MemberInfo>();
    }
}

ÉTAPE 3 - Ajouter un attribut où la sérialisation n'est pas nécessaire mais où la désérialisation l'est.

    [JsonIgnoreSerialization]
    public string Prop1 { get; set; } //Will be skipped when serialized

    [JsonIgnoreSerialization]
    public string Prop2 { get; set; } //Also will be skipped when serialized

    public string Prop3 { get; set; } //Will not be skipped when serialized

ÉTAPE 4 - Utilisez-le

var sweet = JsonConvert.SerializeObject(myObj, new JsonSerializerSettings { ContractResolver = new JsonPropertiesResolver() });

J'espère que cela vous aidera ! Il convient également de noter que cette méthode ignore les propriétés lorsque la désérialisation se produit. Lorsque je dé-sérialise, j'utilise simplement le convertisseur de manière conventionnelle.

JsonConvert.DeserializeObject<MyType>(myString);

8voto

Tho Ho Points 166

Utiliser la propriété setter :

[JsonProperty(nameof(IgnoreOnSerializing))]
public string IgnoreOnSerializingSetter { set { _ignoreOnSerializing = value; } }

[JsonIgnore]
private string _ignoreOnSerializing;

[JsonIgnore]
public string IgnoreOnSerializing
{
    get { return this._ignoreOnSerializing; }
    set { this._ignoreOnSerializing = value; }
}

J'espère que cela vous aidera.

5voto

Ahmed Abulazm Points 59

Après avoir passé un temps assez long à chercher comment marquer une propriété de classe pour qu'elle soit dé-sérialisable et NON sérialisable, j'ai découvert qu'il n'y a rien de tel pour le faire ; j'ai donc trouvé une solution qui combine deux bibliothèques ou techniques de sérialisation différentes (System.Runtime.Serialization.Json & Newtonsoft.Json) et cela a fonctionné pour moi comme suit :

  • marquez toutes vos classes et sous-classes comme "DataContract".
  • marquez toutes les propriétés de votre classe et de vos sous-classes comme "DataMember".
  • marquez toutes les propriétés de votre classe et de vos sous-classes comme "JsonProperty" sauf celles que vous voulez qu'elles ne soient pas sérialisées.
  • Marquez maintenant les propriétés pour lesquelles vous ne voulez pas qu'elles soient sérialisées comme "JsonIgnore".
  • puis Serialize utilisant "Newtonsoft.Json.JsonConvert.SerializeObject" et De-Serialize utilisant "System.Runtime.Serialization.Json.DataContractJsonSerializer".

    using System;
    using System.Collections.Generic;
    using Newtonsoft.Json;
    using System.Runtime.Serialization;
    using System.IO;
    using System.Runtime.Serialization.Json;
    using System.Text;
    
    namespace LUM_Win.model
    {
        [DataContract]
        public class User
        {
            public User() { }
            public User(String JSONObject)
            {
                MemoryStream stream = new MemoryStream(Encoding.Unicode.GetBytes(JSONObject));
                DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(User));
    
                User user = (User)dataContractJsonSerializer.ReadObject(stream);
                this.ID = user.ID;
                this.Country = user.Country;
                this.FirstName = user.FirstName;
                this.LastName = user.LastName;
                this.Nickname = user.Nickname;
                this.PhoneNumber = user.PhoneNumber;
                this.DisplayPicture = user.DisplayPicture;
                this.IsRegistred = user.IsRegistred;
                this.IsConfirmed = user.IsConfirmed;
                this.VerificationCode = user.VerificationCode;
                this.Meetings = user.Meetings;
            }
    
            [DataMember(Name = "_id")]
            [JsonProperty(PropertyName = "_id")]
            public String ID { get; set; }
    
            [DataMember(Name = "country")]
            [JsonProperty(PropertyName = "country")]
            public String Country { get; set; }
    
            [DataMember(Name = "firstname")]
            [JsonProperty(PropertyName = "firstname")]
            public String FirstName { get; set; }
    
            [DataMember(Name = "lastname")]
            [JsonProperty(PropertyName = "lastname")]
            public String LastName { get; set; }
    
            [DataMember(Name = "nickname")]
            [JsonProperty(PropertyName = "nickname")]
            public String Nickname { get; set; }
    
            [DataMember(Name = "number")]
            [JsonProperty(PropertyName = "number")]
            public String PhoneNumber { get; set; }
    
            [DataMember(Name = "thumbnail")]
            [JsonProperty(PropertyName = "thumbnail")]
            public String DisplayPicture { get; set; }
    
            [DataMember(Name = "registered")]
            [JsonProperty(PropertyName = "registered")]
            public bool IsRegistred { get; set; }
    
            [DataMember(Name = "confirmed")]
            [JsonProperty(PropertyName = "confirmed")]
            public bool IsConfirmed { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "verification_code")]
            public String VerificationCode { get; set; }
    
            [JsonIgnore]
            [DataMember(Name = "meeting_ids")]
            public List<Meeting> Meetings { get; set; }
    
            public String toJSONString()
            {
                return JsonConvert.SerializeObject(this, new JsonSerializerSettings() { NullValueHandling = NullValueHandling.Ignore });
            }
        }
    }

J'espère que cela vous aidera...

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X