語言
不可變的 類型 是在物件實例化後,阻止您更改任何屬性或欄位值的類型。 此類型可能是記錄型別、沒有公用屬性或欄位、具有唯讀屬性,或具有私用或僅限初始化的屬性。 System.String 是不可變類型的範例。 System.Text.Json 提供將 JSON 還原串行化為不可變類型的不同方式。
參數化建構函式
根據預設,System.Text.Json 會使用預設的公用無參數建構函式。 不過,您可以指定使用參數化建構函式,這樣就可以反序列化不可變的類別或結構。
針對類別,如果唯一的建構函式是參數化的建構函式,則會使用該建構函式。
如為結構體或具有多個建構函式的類別,請套用 [JsonConstructor] 屬性來指定要使用的建構函式。 未使用 屬性時,如果存在,一律會使用公用無參數建構函式。
下列範例使用
[JsonConstructor]屬性:using System.Text.Json; using System.Text.Json.Serialization; namespace ImmutableTypes { public struct Forecast { public DateTime Date { get; } public int TemperatureC { get; } public string Summary { get; } [JsonConstructor] public Forecast(DateTime date, int temperatureC, string summary) => (Date, TemperatureC, Summary) = (date, temperatureC, summary); } public class Program { public static void Run() { string json = """ { "date":"2020-09-06T11:31:01.923395-07:00", "temperatureC":-1, "summary":"Cold" } """; Console.WriteLine($"Input JSON: {json}"); var options = JsonSerializerOptions.Web; Forecast forecast = JsonSerializer.Deserialize<Forecast>(json, options); Console.WriteLine($"forecast.Date: {forecast.Date}"); Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}"); Console.WriteLine($"forecast.Summary: {forecast.Summary}"); string roundTrippedJson = JsonSerializer.Serialize<Forecast>(forecast, options); Console.WriteLine($"Output JSON: {roundTrippedJson}"); } } } // Produces output like the following example: // //Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"} //forecast.Date: 9 / 6 / 2020 11:31:01 AM //forecast.TemperatureC: -1 //forecast.Summary: Cold //Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}Imports System.Text.Json Imports System.Text.Json.Serialization Namespace ImmutableTypes Public Structure Forecast Public ReadOnly Property [Date] As Date Public ReadOnly Property TemperatureC As Integer Public ReadOnly Property Summary As String <JsonConstructor> Public Sub New([Date] As Date, TemperatureC As Integer, Summary As String) Me.Date = [Date] Me.TemperatureC = TemperatureC Me.Summary = Summary End Sub End Structure Public NotInheritable Class Program Public Shared Sub Main() Dim json As String = "{""date"":""2020-09-06T11:31:01.923395-07:00"",""temperatureC"":-1,""summary"":""Cold""}" Console.WriteLine($"Input JSON: {json}") Dim forecast1 As Forecast = JsonSerializer.Deserialize(Of Forecast)(json, JsonSerializerOptions.Web) Console.WriteLine($"forecast.Date: {forecast1.[Date]}") Console.WriteLine($"forecast.TemperatureC: {forecast1.TemperatureC}") Console.WriteLine($"forecast.Summary: {forecast1.Summary}") Dim roundTrippedJson As String = JsonSerializer.Serialize(forecast1, JsonSerializerOptions.Web) Console.WriteLine($"Output JSON: {roundTrippedJson}") End Sub End Class End Namespace ' Produces output like the following example: ' 'Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"} 'forecast.Date: 9 / 6 / 2020 11:31:01 AM 'forecast.TemperatureC: -1 'forecast.Summary: Cold 'Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","temperatureC":-1,"summary":"Cold"}在 .NET 7 和舊版中,
[JsonConstructor]屬性只能與公用建構函式搭配使用。
在 .NET 8 及以後版本中,反射模式支援標記為 [JsonConstructor]的非公開建構子。 從 .NET 11 開始,原始碼產生模式也支援這些資料。
參數化建構函式的參數名稱必須符合屬性名稱和類型。 比對不區分大小寫,而且建構函式參數必須符合實際的屬性名稱,即使您使用 [JsonPropertyName] 來重新命名屬性。 在下列範例中,TemperatureC 屬性的名稱會變更為 JSON 中的 celsius,但建構函式參數仍名為 temperatureC:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace ImmutableTypesCtorParms
{
public readonly struct Forecast
{
public DateTime Date { get; }
[JsonPropertyName("celsius")]
public int TemperatureC { get; }
public string Summary { get; }
[JsonConstructor]
public Forecast(DateTime date, int temperatureC, string summary) =>
(Date, TemperatureC, Summary) = (date, temperatureC, summary);
}
public class Program
{
public static void Run()
{
string json = """
{
"date":"2020-09-06T11:31:01.923395-07:00",
"celsius":-1,
"summary":"Cold"
}
""";
Console.WriteLine($"Input JSON: {json}");
var options = JsonSerializerOptions.Web;
Forecast forecast = JsonSerializer.Deserialize<Forecast>(json, options);
Console.WriteLine($"forecast.Date: {forecast.Date}");
Console.WriteLine($"forecast.TemperatureC: {forecast.TemperatureC}");
Console.WriteLine($"forecast.Summary: {forecast.Summary}");
string roundTrippedJson =
JsonSerializer.Serialize<Forecast>(forecast, options);
Console.WriteLine($"Output JSON: {roundTrippedJson}");
}
}
}
// Produces output like the following example:
//
//Input JSON: { "date":"2020-09-06T11:31:01.923395-07:00","celsius":-1,"summary":"Cold"}
//forecast.Date: 9 / 6 / 2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "date":"2020-09-06T11:31:01.923395-07:00","celsius":-1,"summary":"Cold"}
除了 [JsonPropertyName]之外,下列屬性也支援透過參數化建構函式來還原序列化:
以參考方式傳遞的建構子參數
從 .NET 11 開始,JsonSerializer 可以反序列化其建構函式參數使用 in、ref、out 和 ref readonly 修飾詞的型別。
| 參數修飾符 | 反序列化行為 |
|---|---|
in、ref 和 ref readonly |
序列化器會以名稱綁定每個參數,並使用其底層元素類型來進行類型匹配。 |
out |
序列化器不會把參數綁定到 JSON。 它會捨棄建構者所指派的值。 |
在下列建構函式中,序列化程式會從 JSON 繫結 temperatureC。 它不會綁定 isValid:
public Forecast(in int temperatureC, out bool isValid)
{
TemperatureC = temperatureC;
isValid = true;
}
在 Visual Basic 中,ByRef建構子參數遵循ref表格所示的行為:
Public Sub New(ByRef temperatureC As Integer)
TemperatureC = temperatureC
End Sub
記錄
序列化和反序列化也支持記錄,如下列範例所示:
using System.Text.Json;
namespace Records
{
public record Forecast(DateTime Date, int TemperatureC)
{
public string? Summary { get; init; }
};
public class Program
{
public static void Run()
{
Forecast forecast = new(DateTime.Now, 40)
{
Summary = "Hot!"
};
string forecastJson = JsonSerializer.Serialize<Forecast>(forecast);
Console.WriteLine(forecastJson);
Forecast? forecastObj = JsonSerializer.Deserialize<Forecast>(forecastJson);
Console.WriteLine(forecastObj);
}
}
}
// Produces output like the following example:
//
//{ "Date":"2020-10-21T15:26:10.5044594-07:00","TemperatureC":40,"Summary":"Hot!"}
//Forecast { Date = 10 / 21 / 2020 3:26:10 PM, TemperatureC = 40, Summary = Hot! }
您可以使用 property: 目標將任何屬性附加到屬性名稱上。 如需位置記錄的詳細資訊,請參閱 C# 語言參考中的 記錄 一文。
非公開成員和屬性存取器
您可以使用 [JsonInclude] 屬性,在屬性上啟用非公用 存取子 的使用,如下列範例所示:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NonPublicAccessors
{
public class Forecast
{
public DateTime Date { get; init; }
[JsonInclude]
public int TemperatureC { get; private set; }
[JsonInclude]
public string? Summary { private get; set; }
};
public class Program
{
public static void Run()
{
string json = """
{
"Date":"2020-10-23T09:51:03.8702889-07:00",
"TemperatureC":40,
"Summary":"Hot"
}
""";
Console.WriteLine($"Input JSON: {json}");
Forecast forecastDeserialized = JsonSerializer.Deserialize<Forecast>(json)!;
Console.WriteLine($"Date: {forecastDeserialized.Date}");
Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}");
json = JsonSerializer.Serialize<Forecast>(forecastDeserialized);
Console.WriteLine($"Output JSON: {json}");
}
}
}
// Produces output like the following example:
//
//Input JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
//Date: 10 / 23 / 2020 9:51:03 AM
//TemperatureC: 40
//Output JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
Imports System.Text.Json
Imports System.Text.Json.Serialization
Namespace NonPublicAccessors
Public Class Forecast
Public Property [Date] As Date
Private _temperatureC As Integer
<JsonInclude>
Public Property TemperatureC As Integer
Get
Return _temperatureC
End Get
Private Set(Value As Integer)
_temperatureC = Value
End Set
End Property
Private _summary As String
<JsonInclude>
Public Property Summary As String
Private Get
Return _summary
End Get
Set(Value As String)
_summary = Value
End Set
End Property
End Class
Public NotInheritable Class Program
Public Shared Sub Main()
Dim json As String = "{""Date"":""2020-10-23T09:51:03.8702889-07:00"",""TemperatureC"":40,""Summary"":""Hot""}"
Console.WriteLine($"Input JSON: {json}")
Dim forecastDeserialized As Forecast = JsonSerializer.Deserialize(Of Forecast)(json)
Console.WriteLine($"Date: {forecastDeserialized.[Date]}")
Console.WriteLine($"TemperatureC: {forecastDeserialized.TemperatureC}")
json = JsonSerializer.Serialize(forecastDeserialized)
Console.WriteLine($"Output JSON: {json}")
End Sub
End Class
End Namespace
' Produces output like the following example:
'
'Input JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
'Date: 10 / 23 / 2020 9:51:03 AM
'TemperatureC: 40
'Output JSON: { "Date":"2020-10-23T09:51:03.8702889-07:00","TemperatureC":40,"Summary":"Hot"}
藉由包含具有私有 setter 的屬性,您仍然可以反序列化該屬性。
在 .NET 8 和更高版本中,您也可以使用 [JsonInclude] 屬性,將非公用 成員 包含到指定類型的序列化合約中。
從 .NET 11 開始,原始碼產生支援以 [JsonInclude] 標記的 private、internal 和 protected 成員。 它也支援對你以 internal 標記的屬性使用 private、protected 和 [JsonInclude] 存取子。 原始碼產生也支援標記為 [JsonConstructor] 的不可存取建構器。
注意
在 .NET 10 及更早版本中,來源產生不支援 `private` 或 `protected` 成員或存取子。 套用 [JsonInclude] 屬性到成員或屬性並不會消除這個限制。 只有在 internal 成員和存取子與產生的 JsonSerializerContext 位於相同組件中時,才支援原始碼產生。 它不支援無法存取的建構器,即使你用 [JsonConstructor]標記了 。
初始化唯讀屬性
System.Text.Json 會如同任何其他可設定的屬性一樣,還原序列化僅 init 的屬性。 從 .NET 11 開始,只有當 JSON 有效載荷包含該屬性時,原始碼產生的設定器才會執行。 被省略的屬性會保留其初始化器值。
唯讀屬性
在 .NET 8 和更新版本中,反序列化也可以應用於只讀屬性或沒有私用或公用設置器的屬性。 雖然您無法變更屬性所參考的實例,但如果屬性的類型是可變的,您可以修改它。 例如,您可以將元素新增至清單。 若要反序列化唯讀屬性,您必須將其物件建立處理行為設定為 填入,而不是 取代。 例如,您可以使用 JsonObjectCreationHandlingAttribute 屬性來標註這個屬性。
class A
{
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
public List<int> Numbers1 { get; } = new List<int>() { 1, 2, 3 };
}
如需詳細資訊,請參閱 填入已初始化屬性。