Setter pribadi di Json.Net

94

Saya tahu ada atribut untuk menangani penyetel pribadi tetapi saya ingin perilaku ini sebagai default, apakah ada cara untuk melakukannya? Kecuali mengubah sumbernya. Akan lebih bagus jika ada pengaturan untuk ini.

Daniel
sumber
1
Saya mencari jawaban ini atau itu .
marbel82

Jawaban:

115

Saya datang ke sini mencari atribut sebenarnya yang membuat Json.NET mengisi properti hanya baca saat deserialisasi, dan itu sederhana [JsonProperty], misalnya:

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

Solusi alternatif

Cukup sediakan konstruktor yang memiliki parameter yang cocok dengan properti Anda:

public class Foo
{
    public string Bar { get; }

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

Sekarang ini berhasil:

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

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

Saya lebih suka pendekatan ini jika memungkinkan karena:

  • Anda tidak perlu mendekorasi properti dengan atribut.
  • Ia bekerja dengan baik { get; private set; }dan adil { get; }.
Saeb Amini
sumber
19
Hanya catatan kecil: ini berfungsi dengan {get;private set;}, bukan dengan{get;}
tymtam
8
Hanya sedikit pembaruan. Sekarang ini juga bekerja dengan {get;};
Hav
1
@Hav Versi berapa sejak itu? Saya baru saja menguji v11.0.2 dan tidak berhasil {get;}
tymtam
1
@ Ttymtam Saya pikir ini hanya bekerja { get; }jika tipe tersebut memiliki konstruktor dengan parameter yang cocok dengan nama properti.
Saeb Amini
2
@tymtam memperbarui jawaban dengan alternatif dan contoh ini.
Saeb Amini
77

Diperbarui, jawaban baru

Saya telah menulis distribusi sumber NuGet untuk ini, yang menginstal satu file dengan dua resolver kontrak khusus:

  • PrivateSetterContractResolver
  • PrivateSetterCamelCasePropertyNamesContractResolver

Instal NuGet:

Install-Package JsonNet.PrivateSettersContractResolvers.Source

Kemudian gunakan salah satu resolver:

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

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

Anda dapat membacanya di sini: http://danielwertheim.se/json-net-private-setters-nuget/

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

Jawaban lama (masih valid)

Ada dua alternatif yang bisa mengatasi masalah tersebut.

Alt 1: Di deserializers

ContractResolver.DefaultMembersSearchFlags =
                             DefaultMembersSearchFlags | BindingFlags.NonPublic;

Opsi serialisasi default mendukung semua jenis anggota kelas. Oleh karena itu, solusi ini akan mengembalikan semua jenis anggota pribadi termasuk bidang. Saya hanya tertarik juga mendukung setter pribadi.

Alt2: Buat ContractResolver kustom:

Oleh karena itu, ini adalah opsi yang lebih baik karena kami baru saja memeriksa properti.

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

Untuk informasi lebih lanjut, baca posting saya: http://danielwertheim.se/json-net-private-setters/

Daniel
sumber
1
@Jafin url sudah mati, danielwertheim.wordpress.com/2010/11/06/… punya sekarang
Chris Marisic
1
Sepertinya Alt 2 jelas merupakan cara terbaik saat ini. DefaultMembersSearchFlagssudah tidak digunakan lagi .
Todd Menier
4
Dengan c # 6, {get; }TIDAK setara dengan { get; private set; }. Untuk cara pertama property.GetSetMethod(true)kembali nulldan yang terakhir true. Ini mengejutkan saya. Anda harus memiliki private set;deserialization untuk bekerja seperti yang diharapkan.
emragins
Sepertinya Install-Package JsonNet.ContractResolvers harus digunakan sekarang. github.com/danielwertheim/jsonnet-contractresolvers
Blok
14

@ Jawaban Daniel (Alt2) tepat, tetapi saya membutuhkan ini untuk bekerja baik untuk penyetel pribadi dan pengambil (saya bekerja dengan API yang sebenarnya memiliki beberapa hal hanya-tulis, seperti user.password.) Inilah yang akhirnya saya dapatkan:

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

Terdaftar sebagai berikut:

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