Prywatne setery w Json.Net

96

Wiem, że istnieje atrybut do obsługi ustawień prywatnych, ale chcę, aby to zachowanie było domyślne, czy istnieje sposób, aby to osiągnąć? Z wyjątkiem poprawiania źródła. Byłoby wspaniale, gdyby było do tego miejsce.

Daniel
źródło
1
Szukałem tego lub tej odpowiedzi.
marbel82

Odpowiedzi:

115

Przyszedłem tutaj, szukając rzeczywistego atrybutu, który sprawia, że ​​Json.NET wypełnia właściwość tylko do odczytu podczas deserializacji, a to po prostu [JsonProperty], np:

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

Alternatywne rozwiązanie

Po prostu podaj konstruktor, który ma parametr pasujący do Twojej właściwości:

public class Foo
{
    public string Bar { get; }

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

Teraz to działa:

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

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

Preferuję takie podejście, jeśli to możliwe, ponieważ:

  • Nie wymaga ozdabiania nieruchomości atrybutami.
  • Działa z obydwoma { get; private set; }i tylko { get; }.
Saeb Amini
źródło
20
Tylko drobna uwaga: działa z {get;private set;}, a nie z{get;}
tymtam
8
Tylko mała aktualizacja. Teraz działa również z {get;};
Hav,
1
@Hav Jaka wersja jest od tego czasu? Właśnie przetestowałem wersję 11.0.2 i nie działa {pobierz;}
tymtam
1
@tymtam Myślę, że działa tylko { get; }wtedy, gdy typ ma konstruktora z parametrem pasującym do nazwy właściwości.
Saeb Amini,
2
@tymtam zaktualizował odpowiedź tą alternatywą i przykładem.
Saeb Amini,
78

Zaktualizowana, nowa odpowiedź

Napisałem w tym celu NuGet dystrybucji źródłowej, który instaluje pojedynczy plik z dwoma niestandardowymi programami do rozpoznawania umów:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

Zainstaluj NuGet:

Install-Package JsonNet.PrivateSettersContractResolvers.Source

Następnie użyj dowolnego z resolwerów:

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

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

Możesz o tym przeczytać tutaj: http://danielwertheim.se/json-net-private-setters-nuget/

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

Stara odpowiedź (nadal ważna)

Istnieją dwie alternatywy, które mogą rozwiązać ten problem.

Alt 1: na deserializatorach

ContractResolver.DefaultMembersSearchFlags =
                             DefaultMembersSearchFlags | BindingFlags.NonPublic;

Domyślna opcja serializacji obsługuje wszystkie typy elementów członkowskich klasy. Dlatego to rozwiązanie zwróci wszystkie prywatne typy członków, w tym pola. Interesuje mnie tylko wspieranie seterów prywatnych.

Alt2: Utwórz niestandardowy ContractResolver:

Dlatego jest to lepsza opcja, ponieważ po prostu sprawdzamy właściwości.

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

Aby uzyskać więcej informacji, przeczytaj mój post: http://danielwertheim.se/json-net-private-setters/

Daniel
źródło
1
Adres URL @Jafin nie żyje, danielwertheim.wordpress.com/2010/11/06/ ... ma go teraz
Chris Marisic
1
Wygląda na to, że Alt 2 jest obecnie zdecydowanie właściwą drogą. DefaultMembersSearchFlagszostał wycofany .
Todd Menier
4
C # 6 {get; }NIE jest równoważne z { get; private set; }. Po pierwsze sposób property.GetSetMethod(true)powraca, nulla drugi true. To mnie zaskoczyło. Musisz mieć, private set;aby deserializacja działała zgodnie z oczekiwaniami.
emragins
Wygląda na to, że należy teraz użyć pakietu Install-Package JsonNet.ContractResolvers. github.com/danielwertheim/jsonnet-contractresolvers
Wyrównany,
14

Odpowiedź @ Daniela (Alt2) jest na miejscu, ale potrzebowałem tego do pracy zarówno dla prywatnych ustawiaczy, jak i pobierających (pracuję z API, które faktycznie ma kilka rzeczy tylko do zapisu, np user.password.) Oto, co skończyło się:

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

Zarejestrowany w ten sposób:

JsonConvert.DefaultSettings = () => new JsonSerializerSettings {
    ContractResolver = new NonPublicPropertiesResolver()
};
Todd Menier
źródło