Setters privés dans Json.Net

93

Je sais qu'il existe un attribut pour gérer les setters privés, mais je veux un peu ce comportement par défaut, y a-t-il un moyen d'accomplir cela? Sauf en modifiant la source. Ce serait génial s'il y avait un cadre pour cela.

Daniel
la source
1
Je cherchais telle ou telle réponse.
marbel82

Réponses:

112

Je suis venu ici à la recherche de l'attribut réel qui fait que Json.NET remplit une propriété en lecture seule lors de la désérialisation, et c'est simplement [JsonProperty], par exemple:

[JsonProperty]
public Guid? ClientId { get; private set; }

Solution alternative

Fournissez simplement un constructeur qui a un paramètre correspondant à votre propriété:

public class Foo
{
    public string Bar { get; }

    public Foo(string bar)
    {
        Bar = bar;
    }
}

Maintenant cela fonctionne:

string json = "{ \"bar\": \"Stack Overflow\" }";

var deserialized = JsonConvert.DeserializeObject<Foo>(json);
Console.WriteLine(deserialized.Bar); // Stack Overflow

Je préfère cette approche dans la mesure du possible car:

  • Il ne vous oblige pas à décorer vos propriétés avec des attributs.
  • Cela fonctionne avec les deux { get; private set; }et juste { get; }.
Saeb Amini
la source
19
Juste une petite note: cela fonctionne avec {get;private set;}, pas avec{get;}
tymtam
8
Juste une petite mise à jour. Maintenant, cela fonctionne aussi avec {get;};
Hav
1
@Hav Quelle est sa version depuis? Je viens de tester la v11.0.2 et cela ne fonctionne pas {get;}
tymtam
1
@tymtam Je pense que cela ne fonctionne que { get; }si le type a un constructeur avec un paramètre correspondant au nom de la propriété.
Saeb Amini
2
@tymtam a mis à jour la réponse avec cette alternative et un exemple.
Saeb Amini
77

Mise à jour, nouvelle réponse

J'ai écrit une distribution source NuGet pour cela, qui installe un seul fichier avec deux résolveurs de contrat personnalisés:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

Installez le NuGet:

Install-Package JsonNet.PrivateSettersContractResolvers.Source

Ensuite, utilisez simplement l'un des résolveurs:

var settings = new JsonSerializerSettings
{
    ContractResolver = new PrivateSetterContractResolver()
};

var model = JsonConvert.DeserializeObject<Model>(json, settings);

Vous pouvez en savoir plus ici: http://danielwertheim.se/json-net-private-setters-nuget/

Repo GitHub: https://github.com/danielwertheim/jsonnet-privatesetterscontractresolvers

Ancienne réponse (toujours valide)

Il existe deux alternatives qui peuvent résoudre le problème.

Alt 1: sur les désérialiseurs

ContractResolver.DefaultMembersSearchFlags =
                             DefaultMembersSearchFlags | BindingFlags.NonPublic;

L'option de sérialisation par défaut prend en charge tous les types de membres de classe. Par conséquent, cette solution renverra tous les types de membres privés, y compris les champs. Je suis seulement intéressé à soutenir également les setters privés.

Alt2: Créez un ContractResolver personnalisé:

Par conséquent, ce sont les meilleures options puisque nous vérifions simplement les propriétés.

public class SisoJsonDefaultContractResolver : DefaultContractResolver 
{
    protected override JsonProperty CreateProperty(
        MemberInfo member,
        MemberSerialization memberSerialization)
    {
        //TODO: Maybe cache
        var prop = base.CreateProperty(member, memberSerialization);

        if (!prop.Writable)
        {
            var property = member as PropertyInfo;
            if (property != null)
            {
                var hasPrivateSetter = property.GetSetMethod(true) != null;
                prop.Writable = hasPrivateSetter;
            }
        }

        return prop;
    }
}

Pour plus d'informations, lisez mon article: http://danielwertheim.se/json-net-private-setters/

Daniel
la source
1
@Jafin url est mort, danielwertheim.wordpress.com/2010/11/06/… l' a maintenant
Chris Marisic
1
On dirait que Alt 2 est définitivement la voie à suivre de nos jours. DefaultMembersSearchFlagsest obsolète .
Todd Menier le
4
Avec c # 6, {get; }n'est PAS équivalent à { get; private set; }. Pour la première manière property.GetSetMethod(true)revient nullet la seconde true. Cela m'a surpris. Vous devez avoir private set;pour que la désérialisation fonctionne comme prévu.
emragins
Il semble que Install-Package JsonNet.ContractResolvers devrait être utilisé maintenant. github.com/danielwertheim/jsonnet-contractresolvers
Aligné
14

La réponse de @ Daniel (Alt2) est parfaite, mais j'en avais besoin pour fonctionner à la fois pour les setters privés et les getters (je travaille avec une API qui contient en fait quelques éléments en écriture seule, comme user.password.) Voici ce que j'ai obtenu:

public class NonPublicPropertiesResolver : DefaultContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) {
        var prop = base.CreateProperty(member, memberSerialization);
        if (member is PropertyInfo pi) {
            prop.Readable = (pi.GetMethod != null);
            prop.Writable = (pi.SetMethod != null);
        }
        return prop;
    }
}

Enregistré ainsi:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
    ContractResolver = new NonPublicPropertiesResolver()
};
Todd Menier
la source