本文說明如何在應用程式中使用來源產生支援的 System.Text.Json 序列化。
如需不同來源產生模式的相關資訊,請參閱來源產生模式。
使用預設的來源產生功能
若要使用來源產生功能搭配所有預設設定(兩種模式及預設選項):
建立衍生自 JsonSerializerContext 的部分類別。
為上下文類別套用 JsonSerializableAttribute,以指定要序列化或還原序列化的類型。
呼叫下列其中一種 JsonSerializer 方法:
- 采用JsonTypeInfo<T>實例,或
- 采用JsonSerializerContext實例,或
- 取得JsonSerializerOptions實例,並將其JsonSerializerOptions.TypeInfoResolver屬性設定為內容類型的
Default屬性。
根據預設,如果您未指定其中一種,則會使用兩種來源產生模式 (中繼資料型 和 序列化最佳化)。 如需如何指定所要使用之模式的詳細資訊,請參閱後文中的指定來源產生模式 。
以下是下列範例中使用的類型:
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
以下是設定為產生前述的 WeatherForecast 類別來源的上下文類別:
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext { }
WeatherForecast 成員的類型不需要以 [JsonSerializable] 屬性明確指定。 宣告為 object 的成員是此規則的例外狀況。 必須針對宣告為 object 的成員指定執行階段類型。 舉例來說,假設您有下列類別:
public class WeatherForecast
{
public object? Data { get; set; }
public List<object>? DataList { get; set; }
}
你知道執行時它可能有 boolean 和 int 物件:
WeatherForecast wf = new() { Data = true, DataList = [true, 1] };
然後 boolean 和 int 必須宣告為 [JsonSerializable]:
[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
public partial class WeatherForecastContext : JsonSerializerContext
{
}
若要指定集合的來源產生,請使用 [JsonSerializable] 搭配集合類型。 例如: [JsonSerializable(typeof(List<WeatherForecast>))] 。
使用來源產生的 JsonSerializer 方法
在下列範例中,內容類型的靜態 Default 屬性會提供採用預設選項的內容類型執行個體。 內容執行個體會提供一個 WeatherForecast 屬性,該屬性會傳回一個 JsonTypeInfo<WeatherForecast> 執行個體。 您可以使用 TypeInfoPropertyName 屬性的 [JsonSerializable] 屬性,為此屬性指定其他名稱。
序列化範例
使用 JsonTypeInfo<T>:
jsonString = JsonSerializer.Serialize(
weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);
sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
jsonString = JsonSerializer.Serialize<WeatherForecast>(weatherForecast, sourceGenOptions);
反序列化範例
使用 JsonTypeInfo<T>:
weatherForecast = JsonSerializer.Deserialize(
jsonString, SourceGenerationContext.Default.WeatherForecast);
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
as WeatherForecast;
var sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString, sourceGenOptions);
完整程式範例
以下是完整程式中的上述範例:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace BothModesNoOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext { }
public class Program
{
public static void Main()
{
string jsonString = """
{
"Date": "2019-08-01T00:00:00",
"TemperatureCelsius": 25,
"Summary": "Hot"
}
""";
WeatherForecast? weatherForecast;
weatherForecast = JsonSerializer.Deserialize(
jsonString, SourceGenerationContext.Default.WeatherForecast);
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
as WeatherForecast;
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM
var sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString, sourceGenOptions);
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM
jsonString = JsonSerializer.Serialize(
weatherForecast!, SourceGenerationContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
sourceGenOptions = new JsonSerializerOptions
{
TypeInfoResolver = SourceGenerationContext.Default
};
jsonString = JsonSerializer.Serialize<WeatherForecast>(weatherForecast, sourceGenOptions);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
}
}
}
指定來源產生模式
您可以為整個環境定義指定 meta 資料型模式或序列化最佳化模式,其中可能包含多個類型。 您也可以為個別的類型指定此模式。 若兩種指定並行,會優先使用為類型指定的模式。
- 對於整個內容,請使用 JsonSourceGenerationOptionsAttribute.GenerationMode 屬性。
- 對於個別的類型,請使用 JsonSerializableAttribute.GenerationMode 屬性。
序列化最佳化 (快速路徑) 模式範例
針對整個上下文
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(WeatherForecast))] internal partial class SerializeOnlyContext : JsonSerializerContext { }對於個別的類型:
[JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Serialization)] internal partial class SerializeOnlyWeatherForecastOnlyContext : JsonSerializerContext { }完整程式範例
using System.Text.Json; using System.Text.Json.Serialization; namespace SerializeOnlyNoOptions { public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureCelsius { get; set; } public string? Summary { get; set; } } [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Serialization)] [JsonSerializable(typeof(WeatherForecast))] internal partial class SerializeOnlyContext : JsonSerializerContext { } [JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Serialization)] internal partial class SerializeOnlyWeatherForecastOnlyContext : JsonSerializerContext { } public class Program { public static void Main() { string jsonString; WeatherForecast weatherForecast = new() { Date = DateTime.Parse("2019-08-01"), TemperatureCelsius = 25, Summary = "Hot" }; // Use context that selects Serialization mode only for WeatherForecast. jsonString = JsonSerializer.Serialize(weatherForecast, SerializeOnlyWeatherForecastOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} // Use a context that selects Serialization mode. jsonString = JsonSerializer.Serialize(weatherForecast, SerializeOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} } } }
中繼資料型模式範例
針對整個上下文
[JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(WeatherForecast))] internal partial class MetadataOnlyContext : JsonSerializerContext { }jsonString = JsonSerializer.Serialize( weatherForecast, MetadataOnlyContext.Default.WeatherForecast);weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyContext.Default.WeatherForecast);對於個別的類型:
[JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Metadata)] internal partial class MetadataOnlyWeatherForecastOnlyContext : JsonSerializerContext { }jsonString = JsonSerializer.Serialize( weatherForecast, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);完整程式範例
using System.Text.Json; using System.Text.Json.Serialization; namespace MetadataOnlyNoOptions { public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureCelsius { get; set; } public string? Summary { get; set; } } [JsonSerializable(typeof(WeatherForecast), GenerationMode = JsonSourceGenerationMode.Metadata)] internal partial class MetadataOnlyWeatherForecastOnlyContext : JsonSerializerContext { } [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)] [JsonSerializable(typeof(WeatherForecast))] internal partial class MetadataOnlyContext : JsonSerializerContext { } public class Program { public static void Main() { string jsonString = """ { "Date": "2019-08-01T00:00:00", "TemperatureCelsius": 25, "Summary": "Hot" } """; WeatherForecast? weatherForecast; // Deserialize with context that selects metadata mode only for WeatherForecast only. weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast); Console.WriteLine($"Date={weatherForecast?.Date}"); // output: //Date=8/1/2019 12:00:00 AM // Serialize with context that selects metadata mode only for WeatherForecast only. jsonString = JsonSerializer.Serialize( weatherForecast, MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} // Deserialize with context that selects metadata mode only. weatherForecast = JsonSerializer.Deserialize<WeatherForecast>( jsonString, MetadataOnlyContext.Default.WeatherForecast); Console.WriteLine($"Date={weatherForecast?.Date}"); // output: //Date=8/1/2019 12:00:00 AM // Serialize with context that selects metadata mode only. jsonString = JsonSerializer.Serialize( weatherForecast, MetadataOnlyContext.Default.WeatherForecast); Console.WriteLine(jsonString); // output: //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"} } } }
ASP.NET Core 的來源產生支援
在 Blazor 應用程式中,使用包含來源生成上下文或 HttpClientJsonExtensions.GetFromJsonAsync 的 HttpClientJsonExtensions.PostAsJsonAsync 和 TypeInfo<TValue> 擴充方法的多載。
從 .NET 8 開始,您也可以使用接受原始檔生成上下文或 HttpClientJsonExtensions.GetFromJsonAsAsyncEnumerable 的 TypeInfo<TValue> 擴充方法的重載。
在 Razor Pages、MVC、Azure SignalR Service 和 Web API 應用程式中,使用 JsonSerializerOptions.TypeInfoResolver 屬性來指定內容。
[JsonSerializable(typeof(WeatherForecast[]))]
internal partial class MyJsonContext : JsonSerializerContext { }
var serializerOptions = new JsonSerializerOptions
{
TypeInfoResolver = MyJsonContext.Default
};
services.AddControllers().AddJsonOptions(
static options =>
options.JsonSerializerOptions.TypeInfoResolverChain.Add(MyJsonContext.Default));
注意
JsonSourceGenerationMode.Serialization或 快速路徑 序列化,不支援非同步序列化。 即使串流序列化需要以中繼資料為基礎的模型,但如果已知承載足夠小,可以符合預先決定的緩衝區大小,則會回復為快速路徑。 如需詳細資訊,請參閱https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/#json。
停用反射預設值
因為 System.Text.Json 預設會使用反映,因此呼叫基本序列化方法可能會中斷原生 AOT 應用程式,而原生 AOT 應用程式不支援所有必要的反映 API。 這些問題可能很難診斷,因為它們可能出現得很突然,而且應用程式通常使用 CoreCLR 執行階段進行偵錯,而在這個環境中,反射功能可以正常運作。 相反地,如果您明確停用反映型序列化,則中斷會更容易診斷出來。 使用反射式序列化的程式碼會在執行時拋 InvalidOperationException 出帶有描述性訊息的訊息。
若要停用應用程式中的預設反映,請將專案檔中的 JsonSerializerIsReflectionEnabledByDefault MSBuild 屬性設定為 false:
<PropertyGroup>
<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
- 不論執行階段、CoreCLR 或 原生 AOT,此屬性的行為都是一致的。
- 如果您未指定此屬性,且已啟用 PublishTrimmed,則系統會自動停用反映型序列化。
您可以使用 JsonSerializer.IsReflectionEnabledByDefault 屬性,以程式設計方式檢查反映是否停用。 下列程式碼片段示範如何根據是否啟用反映來設定序列化程式:
static JsonSerializerOptions CreateDefaultOptions()
{
return new()
{
TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault
? new DefaultJsonTypeInfoResolver()
: MyContext.Default
};
}
由於屬性會被視為連結時間常數,因此上一個方法不會將在原生 AOT 中所執行應用程式中的反映型解析程式設定為根目錄。
指定選項
在 .NET 8 和更新版本中,您可以使用 JsonSerializerOptions 設定的大部分選項也可以使用 JsonSourceGenerationOptionsAttribute 屬性來設定。 透過屬性設定選項的優點是在編譯時間指定組態,這可確保產生的 MyContext.Default 屬性已使用所有相關選項集預先設定。
下列程式碼示範如何使用 JsonSourceGenerationOptionsAttribute 屬性來設定選項。
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext : JsonSerializerContext
{
}
使用 JsonSourceGenerationOptionsAttribute 指定序列化選項時,請呼叫下列其中一個序列化方法:
採用
JsonSerializer.Serialize的TypeInfo<TValue>方法。 傳遞您內容類別的Default.<TypeName>屬性:jsonString = JsonSerializer.Serialize( weatherForecast, SerializationModeOptionsContext.Default.WeatherForecast);採用上下文的
JsonSerializer.Serialize方法。 傳遞您內容類別的Default靜態屬性。jsonString = JsonSerializer.Serialize( weatherForecast, typeof(WeatherForecast), SerializationModeOptionsContext.Default);
若呼叫的方法允許您在自己的 Utf8JsonWriter 執行個體中傳遞,便會接受寫入器的 Indented 設定,而不是 JsonSourceGenerationOptionsAttribute.WriteIndented 選項。
如果您透過呼叫含有 JsonSerializerOptions 執行個體的建構函式來建立和使用 context 執行個體,則將使用提供的執行個體,而不是 JsonSourceGenerationOptionsAttribute 所指定的選項。
下列程式碼顯示完整程式中的上述範例:
using System.Text.Json;
using System.Text.Json.Serialization;
namespace SerializeOnlyWithOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}
[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext : JsonSerializerContext
{
}
public class Program
{
public static void Main()
{
string jsonString;
WeatherForecast weatherForecast = new()
{ Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot" };
// Serialize using TypeInfo<TValue> provided by the context
// and options specified by [JsonSourceGenerationOptions].
jsonString = JsonSerializer.Serialize(
weatherForecast, SerializationModeOptionsContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{
// "date": "2019-08-01T00:00:00",
// "temperatureCelsius": 0,
// "summary": "Hot"
//}
// Serialize using Default context
// and options specified by [JsonSourceGenerationOptions].
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), SerializationModeOptionsContext.Default);
Console.WriteLine(jsonString);
// output:
//{
// "date": "2019-08-01T00:00:00",
// "temperatureCelsius": 0,
// "summary": "Hot"
//}
}
}
}
合併源代碼生成器
您可以在單一 JsonSerializerOptions 執行個體內結合多個來源生成的上下文中的合約。 使用 JsonSerializerOptions.TypeInfoResolver 屬性來鏈結已使用 JsonTypeInfoResolver.Combine(IJsonTypeInfoResolver[]) 方法結合的多個內容。
var options = new JsonSerializerOptions
{
TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default, ContextB.Default, ContextC.Default),
};
從 .NET 8 開始,如果您稍後想要在前面加上或附加另一個內容,則可以使用 JsonSerializerOptions.TypeInfoResolverChain 屬性來執行此動作。 鏈結的順序相當重要:JsonSerializerOptions 會按照指定的順序查詢每個解析器,並返回第一個非空的結果。
options.TypeInfoResolverChain.Add(ContextD.Default); // Append to the end of the list.
options.TypeInfoResolverChain.Insert(0, ContextE.Default); // Insert at the beginning of the list.
對 TypeInfoResolverChain 屬性所做的任何變更會由 TypeInfoResolver 反映,反之亦然。
將列舉欄位序列化為字串
預設狀態下,會將列舉序列化為數字。 若要在使用來源產生時將特定列舉的欄位序列化為字串,請使用 JsonStringEnumConverter<TEnum> 轉換器標註它。 或者,若要設定所有列舉的地毯式原則,請使用 JsonSourceGenerationOptionsAttribute 屬性。
JsonStringEnumConverter<T> 轉換器
若要使用來源產生將列舉名稱序列化為字串,請使用 JsonStringEnumConverter<TEnum> 轉換器。 (原生 AOT 執行階段不支援非泛型 JsonStringEnumConverter 類型。)
使用 JsonStringEnumConverter<TEnum> 屬性,以 JsonConverterAttribute 轉換器標註列舉型別:
public class WeatherForecastWithPrecipEnum
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Precipitation? Precipitation { get; set; }
}
[JsonConverter(typeof(JsonStringEnumConverter<Precipitation>))]
public enum Precipitation
{
Drizzle, Rain, Sleet, Hail, Snow
}
建立 JsonSerializerContext 類別,並使用 JsonSerializableAttribute 屬性標註它:
[JsonSerializable(typeof(WeatherForecastWithPrecipEnum))]
public partial class Context1 : JsonSerializerContext { }
下列程式碼會將列舉名稱序列化,而不是數值:
var weatherForecast = new WeatherForecastWithPrecipEnum
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Precipitation = Precipitation.Sleet
};
var options = new JsonSerializerOptions
{
WriteIndented = true,
TypeInfoResolver = Context1.Default,
};
string? jsonString = JsonSerializer.Serialize(weatherForecast, options);
產生的 JSON 類似下列範例:
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Precipitation": "Sleet"
}
全面性政策
您可以套用一個全面政策,以將列舉序列化為字串來使用 JsonStringEnumConverter<TEnum>,而不是使用 JsonSourceGenerationOptionsAttribute 型別。 建立 JsonSerializerContext 類別,並使用 JsonSerializableAttribute 和 JsonSourceGenerationOptionsAttribute 屬性標註它:
[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(WeatherForecast2WithPrecipEnum))]
public partial class Context2 : JsonSerializerContext { }
請注意,列舉型別沒有 JsonConverterAttribute:
public class WeatherForecast2WithPrecipEnum
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Precipitation2? Precipitation { get; set; }
}
public enum Precipitation2
{
Drizzle, Rain, Sleet, Hail, Snow
}
自定義列舉成員名稱
從 .NET 9 開始,您可以使用 JsonStringEnumMemberName 屬性來自訂列舉成員名稱。 如需詳細資訊,請參閱 自定義列舉成員名稱。