web-dev-qa-db-fra.com

Faire une propriété désérialiser mais pas sérialiser avec json.net

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

Nous souhaitons que l'une des propriétés de la classe sérialisée ne soit plus une propriété enum mais une propriété de classe.

Un moyen simple de le faire serait de laisser l'ancienne propriété enum sur la classe et de demander à Json.net de lire cette propriété lorsque nous chargerons la configuration, mais de ne pas la sauvegarder à nouveau lors de la prochaine sérialisation de l'objet. Nous allons traiter la génération de la nouvelle classe à partir de l’ancien enum séparément.

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

79
Will Dean

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

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

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 vous voulez faire ceci:

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 ceci:

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

Approche 1: Ajouter une méthode ShouldSerialize

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

Pour utiliser cette fonctionnalité, ajoutez une méthode booléenne ShouldSerializeBlah() à votre classe, où Blah est remplacé par le nom de la propriété que vous ne souhaitez pas sérialiser. Faire que l'implémentation de cette méthode retourne 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 méthode ShouldSerialize, vous pouvez utiliser une IContractResolver pour faire la même chose par programme. Voir Sérialisation des propriétés conditionnelles dans la documentation.

Approche 2: Manipuler le JSON avec JObjects

Au lieu d'utiliser JsonConvert.SerializeObject pour effectuer la sérialisation, chargez l'objet config dans un JObject, puis supprimez simplement la propriété non désirée du JSON avant de l'écrire. C'est juste 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: Utilisation intelligente des attributs

  1. Appliquez un attribut [JsonIgnore] à la propriété que vous ne souhaitez pas numéroter.
  2. Ajoutez un autre utilisateur, private, un créateur de propriétés à la classe du 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é d'origine.
  3. Appliquez un attribut [JsonProperty] à l'autre utilisateur, en lui attribuant le même nom JSON que la propriété d'origine.

Voici la classe Config révisée:

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; }
}
89
Brian Rogers

J'aime coller avec les attributs sur celle-ci, voici la méthode que j'utilise pour désérialiser une propriété mais pas pour la sérialiser ou vice versa.

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

public class JsonIgnoreSerializationAttribute : Attribute { }

ETAPE 2 - Créer un contrat personnalisé Reslover

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 là où la sérialisation n'est pas nécessaire mais la désérialisation 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 t'aides! En outre, il est à noter que cela ignorera également les propriétés lorsque la désérialisation se produit, lorsque je désérialise, j'utilise simplement le convertisseur de manière classique.

JsonConvert.DeserializeObject<MyType>(myString);
20
Jraco11

Utilisez 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 cette aide.

6
Tho Ho

Après avoir passé assez de temps à chercher comment marquer une propriété de classe comme étant dé-sérialisable et non sérialisable, j'ai constaté qu'il n'y a rien de tel pour le faire; Je propose donc une solution combinant deux bibliothèques ou techniques de sérialisation différentes (System.Runtime.Serialization.Json & Newtonsoft.Json) et fonctionne 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 en tant que "DataMember".
  • marquez toutes les propriétés de votre classe et de vos sous-classes comme "JsonProperty", à l'exception de celles pour lesquelles vous souhaitez qu'elles ne soient pas sérialisées.
  • indiquez maintenant les propriétés que vous ne voulez PAS qu'il soit sérialisé en tant que "JsonIgnore".
  • puis Sérialiser en utilisant "Newtonsoft.Json.JsonConvert.SerializeObject" et Sérialiser en 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 pourra aider ...

5
Ahmed Abulazm

Pour toute situation dans laquelle il est acceptable que votre propriété réservée à la désérialisation soit marquée comme étant interne, il existe une solution remarquablement simple qui ne dépend pas du tout des attributs. Marquez simplement la propriété comme get interne, mais ensemble public:

public class JsonTest {

    public string SomeProperty { internal get; set; }

}

Cela aboutit à une désérialisation correcte à l'aide des paramètres/résolveurs par défaut/etc., Mais la propriété est supprimée de la sortie sérialisée.

5
Iucounu

en référence à la solution de @ ThoHo, utiliser le setter est en fait tout ce qui est nécessaire, sans balises supplémentaires.

Pour moi, j’avais auparavant un seul ID de référence, que je voulais charger et ajouter à la nouvelle collection d’ID de référence. En modifiant la définition de l'ID de référence pour ne contenir qu'une méthode de définition, qui a ajouté la valeur à la nouvelle collection. Json ne peut pas écrire la valeur si la propriété n'a pas get; méthode.

// Old property that I want to read from Json, but never write again. No getter.
public Guid RefId { set { RefIds.Add(value); } }

// New property that will be in use from now on. Both setter and getter.
public ICollection<Guid> RefIds { get; set; }

Cette classe est désormais rétro-compatible avec la version précédente et enregistre uniquement RefIds pour les nouvelles versions.

1
simo.37920

Si vous utilisez JsonConvert, IgnoreDataMemberAttribute est ok.Ma bibliothèque standard ne référence pas Newton.Json et j'utilise [IgnoreDataMember] pour contrôler la sérialisation des objets.

De Newton.net document d'aide.

0
menxin

Pour construire sur la réponse de Tho Ho, cela peut également être utilisé pour les champs.

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

[JsonIgnore]
public string IgnoreOnSerializing;
0

En fonction de l'endroit où cela se produit dans l'application et s'il ne s'agit que d'une propriété, vous pouvez le faire de manière manuelle en définissant la valeur de la propriété sur null, puis sur le modèle, vous pouvez spécifier que la propriété est ignoré si la valeur est null:

[JsonProperty(NullValueHandling = NullValue.Ignore)]
public string MyProperty { get; set; }

Si vous travaillez sur une application Web ASP.NET Core, vous pouvez le définir globalement pour toutes les propriétés de tous les modèles en le définissant dans votre fichier Startup.cs:

public void ConfigureServices(IServiceCollection services) {
    // other configuration here
    services.AddMvc()
        .AddJsonOptions(options => options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore);
}
0
OzzyTheGiant

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

Le moyen le plus simple que j'ai trouvé au moment d'écrire ces lignes est d'inclure cette logique dans votre IContractResolver .

Exemple de code du lien ci-dessus copié ici pour la postérité:

public class Employee
{
    public string Name { get; set; }
    public Employee Manager { get; set; }

    public bool ShouldSerializeManager()
    {
        // don't serialize the Manager property if an employee is their own manager
        return (Manager != this);
    }
}

public class ShouldSerializeContractResolver : DefaultContractResolver
{
    public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver();

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        JsonProperty property = base.CreateProperty(member, memberSerialization);

        if (property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager")
        {
            property.ShouldSerialize =
                instance =>
                {
                    Employee e = (Employee)instance;
                    return e.Manager != e;
                };
        }

        return property;
    }
}

Toutes les réponses sont bonnes mais cette approche semblait être la plus propre. En fait, je l'ai implémenté en recherchant un attribut sur la propriété pour SkipSerialize et SkipDeserialize afin que vous puissiez simplement marquer toute classe que vous contrôlez. Bonne question!

0