Mengisi properti yang diinisialisasi
Mulai dari .NET 8, Anda dapat menentukan preferensi untuk mengganti atau mengisi properti .NET saat JSON dideserialisasi. JsonObjectCreationHandling Enum menyediakan pilihan penanganan pembuatan objek:
Perilaku default (ganti)
Deserializer System.Text.Json selalu membuat instans baru dari jenis target. Namun, meskipun instans baru dibuat, beberapa properti dan bidang mungkin sudah diinisialisasi sebagai bagian dari konstruksi objek. Pertimbangkan jenis berikut:
class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}
Saat Anda membuat instans kelas ini, Numbers1
nilai properti (dan Numbers2
) adalah daftar dengan tiga elemen (1, 2, dan 3). Jika Anda mendeserialisasi JSON ke jenis ini, perilaku defaultnya adalah bahwa nilai properti diganti:
- Untuk
Numbers1
, karena baca-saja (tanpa setter), nilainya masih 1, 2, dan 3 dalam daftarnya. - Untuk
Numbers2
, yang bersifat baca-tulis, daftar baru dialokasikan dan nilai dari JSON ditambahkan.
Misalnya, jika Anda menjalankan kode deserialisasi berikut, Numbers1
berisi nilai 1, 2, dan 3 dan Numbers2
berisi nilai 4, 5, dan 6.
A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");
Mengisi perilaku
Mulai dari .NET 8, Anda dapat mengubah perilaku deserialisasi untuk mengubah (mengisi) properti dan bidang alih-alih menggantinya:
Untuk properti jenis koleksi, objek digunakan kembali tanpa menghapus. Jika koleksi telah diisi dengan elemen, koleksi akan ditampilkan dalam hasil deserialisasi akhir bersama dengan nilai dari JSON. Misalnya, lihat Contoh properti koleksi.
Untuk properti yang merupakan objek dengan properti, propertinya yang dapat diubah diperbarui ke nilai JSON tetapi referensi objek itu sendiri tidak berubah.
Untuk properti jenis struct, perilaku efektifnya adalah bahwa untuk propertinya yang dapat diubah, nilai apa pun yang ada disimpan dan nilai baru dari JSON ditambahkan. Namun, tidak seperti properti referensi, objek itu sendiri tidak digunakan kembali karena merupakan jenis nilai. Sebagai gantinya, salinan struktur dimodifikasi dan kemudian ditetapkan kembali ke properti . Misalnya, lihat Contoh properti Struct.
Properti struct harus memiliki setter; jika tidak, dilemparkan InvalidOperationException pada waktu proses.
Catatan
Perilaku pengisian saat ini tidak berfungsi untuk jenis yang memiliki konstruktor berparameter. Untuk informasi selengkapnya, lihat masalah dotnet/runtime 92877.
Properti baca-saja
Untuk mengisi properti referensi yang dapat diubah, karena instans yang dirujuk properti tidak diganti, properti tidak perlu memiliki setter. Perilaku ini berarti bahwa deserialisasi juga dapat mengisi properti baca-saja .
Catatan
Properti struct masih memerlukan setter karena instans diganti dengan salinan yang dimodifikasi.
Contoh properti koleksi
Pertimbangkan kelas A
yang sama dari contoh perilaku penggantian, tetapi kali ini diannotasikan dengan preferensi untuk mengisi properti alih-alih menggantinya:
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}
Jika Anda menjalankan kode deserialisasi berikut, keduanya Numbers1
dan Numbers2
berisi nilai 1, 2, 3, 4, 5, dan 6:
A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");
Contoh properti struct
Kelas berikut berisi properti struct, S1
, yang perilaku deserialisasinya diatur ke Populate. Setelah menjalankan kode ini, c.S1.Value1
memiliki nilai 10 (dari konstruktor) dan c.S1.Value2
memiliki nilai 5 (dari JSON).
C? c = JsonSerializer.Deserialize<C>("""{"S1": {"Value2": 5}}""");
class C
{
public C()
{
_s1 = new S
{
Value1 = 10
};
}
private S _s1;
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
public S S1
{
get { return _s1; }
set { _s1 = value; }
}
}
struct S
{
public int Value1 { get; set; }
public int Value2 { get; set; }
}
Jika perilaku default Replace digunakan sebagai gantinya, c.S1.Value1
akan memiliki nilai default 0 setelah deserialisasi. Itu karena konstruktor C()
akan dipanggil, diatur c.S1.Value1
ke 10, tetapi kemudian nilai S1 akan diganti dengan instans baru. (c.S1.Value2
masih akan menjadi 5, karena JSON menggantikan nilai default.)
Cara menentukan
Ada beberapa cara untuk menentukan preferensi untuk mengganti atau mengisi:
JsonObjectCreationHandlingAttribute Gunakan atribut untuk membuat anotasi pada jenis atau tingkat properti. Jika Anda mengatur atribut pada tingkat jenis dan mengatur propertinya Handling ke Populate, perilaku hanya akan berlaku untuk properti tersebut di mana populasi dimungkinkan (misalnya, jenis nilai harus memiliki setter).
Jika Anda ingin preferensi di seluruh jenis menjadi Populate, tetapi ingin mengecualikan satu atau beberapa properti dari perilaku tersebut, Anda dapat menambahkan atribut pada tingkat jenis dan sekali lagi di tingkat properti untuk mengambil alih perilaku yang diwariskan. Pola tersebut ditampilkan dalam kode berikut.
// Type-level preference is Populate. [JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] class B { // For this property only, use Replace behavior. [JsonObjectCreationHandling(JsonObjectCreationHandling.Replace)] public List<int> Numbers1 { get; } = [1, 2, 3]; public List<int> Numbers2 { get; set; } = [1, 2, 3]; }
Atur JsonSerializerOptions.PreferredObjectCreationHandling (atau, untuk pembuatan sumber, JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandling) untuk menentukan preferensi global.
var options = new JsonSerializerOptions { PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate };