Private Setter in Json.Net

92

Ich weiß, dass es ein Attribut gibt, mit dem private Setter behandelt werden können, aber ich möchte dieses Verhalten standardmäßig. Gibt es eine Möglichkeit, dies zu erreichen? Außer die Quelle zu optimieren. Wäre toll, wenn es dafür eine Einstellung gäbe.

Daniel
quelle
1
Ich habe nach dieser oder jener Antwort gesucht .
Marbel82

Antworten:

111

Ich bin hierher gekommen, um nach dem tatsächlichen Attribut zu suchen, mit dem Json.NET beim Deserialisieren eine schreibgeschützte Eigenschaft auffüllt, und das ist einfach [JsonProperty], z.

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

Alternative Lösung

Geben Sie einfach einen Konstruktor an, dessen Parameter mit Ihrer Eigenschaft übereinstimmt:

public class Foo
{
    public string Bar { get; }

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

Nun funktioniert das:

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

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

Ich bevorzuge diesen Ansatz, wenn möglich, da:

  • Sie müssen Ihre Eigenschaften nicht mit Attributen dekorieren.
  • Es funktioniert mit beiden { get; private set; }und nur { get; }.
Saeb Amini
quelle
19
Nur eine kleine Anmerkung: es funktioniert mit {get;private set;}, nicht mit{get;}
Tymtam
8
Nur ein kleines Update. Jetzt funktioniert es auch mit {get;};
Hav
1
@ Hav Welche Version ist es seit? Ich habe gerade v11.0.2 getestet und es funktioniert nicht {get;}
tymtam
1
@tymtam Ich denke, es funktioniert nur, { get; }wenn der Typ einen Konstruktor mit einem Parameter hat, der mit dem Eigenschaftsnamen übereinstimmt.
Saeb Amini
2
@tymtam hat die Antwort mit dieser Alternative und einem Beispiel aktualisiert.
Saeb Amini
77

Aktualisierte, neue Antwort

Ich habe dafür eine Quelldistribution NuGet geschrieben, die eine einzelne Datei mit zwei benutzerdefinierten Vertragsauflösern installiert:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

Installieren Sie das NuGet:

Install-Package JsonNet.PrivateSettersContractResolvers.Source

Verwenden Sie dann einfach einen der Resolver:

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

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

Sie können darüber hier lesen: http://danielwertheim.se/json-net-private-setters-nuget/

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

Alte Antwort (noch gültig)

Es gibt zwei Alternativen, die das Problem lösen können.

Alt 1: Auf den Deserialisierern

ContractResolver.DefaultMembersSearchFlags =
                             DefaultMembersSearchFlags | BindingFlags.NonPublic;

Die Standard-Serialisierungsoption unterstützt alle Arten von Klassenmitgliedern. Daher gibt diese Lösung alle privaten Mitgliedstypen einschließlich Felder zurück. Ich bin nur daran interessiert, auch private Setter zu unterstützen.

Alt2: Erstellen Sie einen benutzerdefinierten ContractResolver:

Daher ist dies die bessere Option, da wir nur die Eigenschaften überprüfen.

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

Weitere Informationen finden Sie in meinem Beitrag: http://danielwertheim.se/json-net-private-setters/

Daniel
quelle
2
Link
Jafin
1
@ Jafin URL ist tot, danielwertheim.wordpress.com/2010/11/06/… hat es jetzt
Chris Marisic
1
Sieht so aus, als wäre Alt 2 heutzutage definitiv der richtige Weg. DefaultMembersSearchFlagswurde veraltet .
Todd Menier
4
Mit c # 6 {get; }ist NICHT gleichbedeutend mit { get; private set; }. Für den ersten Weg property.GetSetMethod(true)kehrt nullder letztere zurück true. Das hat mich überrascht. Sie müssen haben, private set;damit die Deserialisierung wie erwartet funktioniert.
Emragins
Es sieht so aus, als ob das Installationspaket JsonNet.ContractResolvers jetzt verwendet werden sollte. github.com/danielwertheim/jsonnet-contractresolvers
Ausgerichtet
14

@ Daniels Antwort (Alt2) ist genau richtig, aber ich brauchte sie, um sowohl für private Setter als auch für Getter zu funktionieren (ich arbeite mit einer API, die tatsächlich ein paar schreibgeschützte Dinge enthält, wie z user.password. B.) .

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

So registriert:

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