共用方式為


填入初始化的屬性

從 .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];
}

如果您執行下列還原序列化程式碼,Numbers1Numbers2 都會包含值 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
    };