填入初始化的屬性
從 .NET 8 開始,您可以在還原序列化 JSON 時指定取代或填入 .NET 屬性的喜好設定。 JsonObjectCreationHandling 列舉提供物件建立處理選項:
預設 (取代) 行為
System.Text.Json 還原序列化程式一律會建立目標型別的新執行個體。 不過,即使已建立新的執行個體,某些屬性和欄位可能已初始化為物件建構的一部分。 請考慮下列 類型:
class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}
當您建立這個類別的執行個體時,Numbers1
(和 Numbers2
) 屬性的值是具有三個元素 (1、2 和 3) 的清單。 如果您將 JSON 還原序列化為此類型,則預設行為是取代屬性值:
- 對於
Numbers1
,因為它屬於唯讀 (沒有 setter),所以其清單中仍然有值 1、2 和 3。 - 對於
Numbers2
,其屬於讀寫,會配置新的清單,並新增來自 JSON 的值。
例如,如果您執行下列還原序列化程式碼,則 Numbers1
包含值 1、2 和 3,而 Numbers2
包含值 4、5 和 6。
A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");
填入行為
從 .NET 8 開始,您可以變更還原序列化行為來修改 (填入) 屬性和欄位,而不是取代它們:
對於集合型別屬性,物件會重複使用而不清除。 如果集合已預先填入元素,它們將會顯示在最終還原序列化的結果中,以及 JSON 中的值。 如需範例,請參閱集合屬性範例。
如果是物件具有屬性的屬性,其可變動屬性會更新為 JSON 值,但物件參考本身不會變更。
對於結構型別屬性,有效的行為是,針對其可變動的屬性,會保留任何現有的值,並新增來自 JSON 的新值。 不過,不同於參考屬性,物件本身不會重複使用,因為它是實值型別。 相反地,會修改結構的複本,然後重新指派給屬性。 如需範例,請參閱結構屬性範例。
結構屬性必須有 setter;否則,InvalidOperationException 會在執行階段擲回。
注意
填入行為目前不適用於具有參數化建構函式的型別。 如需詳細資訊,請參閱 dotnet/執行階段問題 92877。
唯讀屬性
若要填入可變動的參考屬性,因為不會取代屬性參考的執行個體,屬性不需要有 setter。 此行為表示還原序列化也可以填入唯讀屬性。
注意
結構屬性仍然需要 setter,因為執行個體會取代為修改後的複本。
集合屬性範例
請考慮取代行為範例中的相同類別 A
,但這次以填入屬性的喜好設定加上註解,而不是取代屬性:
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}
如果您執行下列還原序列化程式碼,Numbers1
和 Numbers2
都會包含值 1、2、3、4、5 和 6:
A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2": [4,5,6]}""");
結構屬性範例
下列類別包含結構屬性 S1
,其還原序列化行為設定為 Populate。 執行此程式碼之後,c.S1.Value1
具有值 10 (來自建構函式),而 c.S1.Value2
具有值 5 (來自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; }
}
如果改用預設 Replace 行為,則 c.S1.Value1
在還原序列化之後的預設值會是 0。 這是因為會呼叫建構函式 C()
,並將 c.S1.Value1
設定為 10,但 S1 的值會取代為新的執行個體。 (c.S1.Value2
仍然會是 5,因為 JSON 會取代預設值。)
如何指定
有多種方式可以指定取代或填入的喜好設定:
使用 JsonObjectCreationHandlingAttribute 屬性在型別或屬性層級加上註解。 如果您在類型層級設定屬性並將其 Handling 屬性設定為 Populate,則行為只會套用至可能母體擴展的屬性 (例如,實值型別必須有 setter)。
如果您想要讓全型別喜好設定成為 Populate,但想要從該行為中排除一或多個屬性,則可以在類型層級新增屬性,並在屬性層級再次新增屬性,以覆寫繼承的行為。 此模式會顯示在下列程式碼中。
// 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]; }
設定 JsonSerializerOptions.PreferredObjectCreationHandling (或,針對來源產生,JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandling) 指定全域喜好設定。
var options = new JsonSerializerOptions { PreferredObjectCreationHandling = JsonObjectCreationHandling.Populate };