Setter privati ​​in Json.Net


93

So che esiste un attributo per gestire i setter privati, ma voglio che questo comportamento sia predefinito, c'è un modo per farlo? Tranne modificare la fonte. Sarebbe fantastico se ci fosse un ambiente per questo.


1
Stavo cercando questa o quella risposta.
marbel82

Risposte:


112

Sono venuto qui alla ricerca dell'attributo effettivo che fa sì che Json.NET popoli una proprietà di sola lettura durante la deserializzazione, e questo è semplicemente [JsonProperty], ad esempio:

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

Soluzione alternativa

Fornisci semplicemente un costruttore che abbia un parametro che corrisponde alla tua proprietà:

public class Foo
{
    public string Bar { get; }

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

Ora funziona:

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

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

Preferisco questo approccio ove possibile poiché:

  • Non richiede di decorare le tue proprietà con attributi.
  • Funziona con entrambi { get; private set; }e solo { get; }.

19
Solo una piccola nota: funziona con {get;private set;}, non con{get;}
tymtam

8
Solo un piccolo aggiornamento. Ora funziona anche con {get;};
Hav

1
@Hav Quale versione è da allora? Ho appena testato la v11.0.2 e non funziona {get;}
tymtam

1
@tymtam Penso che funzioni solo { get; }se il tipo ha un costruttore con un parametro che corrisponde al nome della proprietà.
Saeb Amini

2
@tymtam ha aggiornato la risposta con questa alternativa e un esempio.
Saeb Amini

77

Aggiornata, nuova risposta

Ho scritto una distribuzione di origine NuGet per questo, che installa un singolo file con due risolutori di contratto personalizzati:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

Installa NuGet:

Install-Package JsonNet.PrivateSettersContractResolvers.Source

Quindi usa uno dei resolver:

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

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

Puoi leggerlo qui: http://danielwertheim.se/json-net-private-setters-nuget/

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

Vecchia risposta (ancora valida)

Esistono due alternative che possono risolvere il problema.

Alt 1: sui deserializzatori

ContractResolver.DefaultMembersSearchFlags =
                             DefaultMembersSearchFlags | BindingFlags.NonPublic;

L'opzione di serializzazione predefinita supporta tutti i tipi di membri della classe. Pertanto questa soluzione restituirà tutti i tipi di membri privati ​​inclusi i campi. Mi interessa solo supportare anche i setter privati.

Alt2: crea un ContractResolver personalizzato:

Pertanto questa è l'opzione migliore poiché controlliamo solo le proprietà.

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

Per ulteriori informazioni, leggi il mio post: http://danielwertheim.se/json-net-private-setters/




1
Sembra che Alt 2 sia decisamente la strada da percorrere al giorno d'oggi. DefaultMembersSearchFlagsè stato deprecato .
Todd Menier

4
Con c # 6, {get; }NON è equivalente a { get; private set; }. Per il primo modo property.GetSetMethod(true)ritorna nulle per il secondo true. Questo mi ha sorpreso. È necessario disporre di private set;affinché la deserializzazione funzioni come previsto.
emragini

Sembra che il pacchetto di installazione JsonNet.ContractResolvers dovrebbe essere utilizzato ora. github.com/danielwertheim/jsonnet-contractresolvers
Allineato il

14

La risposta di @ Daniel (Alt2) è perfetta, ma avevo bisogno che funzionasse sia per i setter privati ​​che per i getter (sto lavorando con un'API che in realtà ha alcune cose di sola scrittura, come user.password.) Ecco cosa ho finito con:

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

Registrato così:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
    ContractResolver = new NonPublicPropertiesResolver()
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.