如何在 System.Text.Json 中使用源生成

System.Text.Json 中的源生成在 .NET 6 及更高版本中提供。 在应用中使用时,应用的语言版本必须为 C# 9.0 或更高版本。 本文介绍如何在应用中使用源生成支持的序列化。

有关不同源生成模式的信息,请参阅源生成模式

使用源生成默认值

若要将源生成用于所有默认值(两种模式,默认选项):

  1. 创建一个从 JsonSerializerContext 派生的分部类。

  2. 通过向上下文类应用 JsonSerializableAttribute 来指定要序列化或反序列化的类型。

  3. 调用 JsonSerializer 方法:

默认情况下,如果未指定源生成模式,则会使用这两种源生成模式。 若要了解如何指定要使用的模式,请参阅本文后面的 指定源生成模式

下面是以下示例中使用的类型:

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
{
}

无需使用 [JsonSerializable] 属性显式指定 WeatherForecast 成员的类型。 声明为 object 的成员是此规则的例外。 需要指定声明为 object 的成员的运行时类型。 例如,假设有以下类:

public class WeatherForecast
{
    public object? Data { get; set; }
    public List<object>? DataList { get; set; }
}

你知道,在运行时,它可能有 booleanint 对象:

WeatherForecast wf = new() { Data = true, DataList = new List<object> { true, 1 } };

其次,必须将 booleanint 声明为 [JsonSerializable]

[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
public partial class WeatherForecastContext : JsonSerializerContext
{
}

若要指定集合的源生成,请将 [JsonSerializable] 与集合类型一起使用。 例如:[JsonSerializable(typeof(List<WeatherForecast>))]

使用源生成的 JsonSerializer 方法

在以下示例中,上下文类型的静态 Default 属性提供具有默认选项的上下文类型的实例。 上下文实例提供返回 JsonTypeInfo<WeatherForecast> 实例的 WeatherForecast 属性。 可以使用 [JsonSerializable] 特性的 TypeInfoPropertyName 属性为此属性指定其他名称。

序列化示例

使用 JsonTypeInfo<T>

jsonString = JsonSerializer.Serialize(
    weatherForecast!, SourceGenerationContext.Default.WeatherForecast);

使用 JsonSerializerContext

jsonString = JsonSerializer.Serialize(
    weatherForecast, typeof(WeatherForecast), SourceGenerationContext.Default);

使用 JsonSerializerOptions

sourceGenOptions = new JsonSerializerOptions
{
    TypeInfoResolver = SourceGenerationContext.Default
};

jsonString = JsonSerializer.Serialize(
    weatherForecast, typeof(WeatherForecast), sourceGenOptions);

反序列化示例

使用 JsonTypeInfo<T>

weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
    jsonString, SourceGenerationContext.Default.WeatherForecast);

使用 JsonSerializerContext

weatherForecast = JsonSerializer.Deserialize(
    jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
    as WeatherForecast;

使用 JsonSerializerOptions

var sourceGenOptions = new JsonSerializerOptions
{
    TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
    jsonString, typeof(WeatherForecast), sourceGenOptions)
    as WeatherForecast;

完整的程序示例

下面是上述示例在完整程序中的情况:

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<WeatherForecast>(
                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(
                jsonString, typeof(WeatherForecast), sourceGenOptions)
                as WeatherForecast;
            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, typeof(WeatherForecast), sourceGenOptions);
            Console.WriteLine(jsonString);
            // output:
            //{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
        }
    }
}

指定源生成模式

可以为完整上下文指定基于元数据的模式或序列化优化模式,其中可能包括多个类型。 也可以为单个类型指定模式。 如果同时执行这两项操作,则类型的模式规范将优先。

序列化优化(快速路径)模式示例

  • 对于完整上下文:

    [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.GetFromJsonAsyncHttpClientJsonExtensions.PostAsJsonAsync 扩展方法(采用源生成上下文或 TypeInfo<TValue>)的重载。

从 .NET 8 开始,你还可以使用接受源生成上下文或 TypeInfo<TValue>HttpClientJsonExtensions.GetFromJsonAsAsyncEnumerable 扩展方法的重载。

在 Razor Pages、MVC、SignalR 和 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));

在 Razor Pages、MVC、SignalR 和 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 = serializerOptions);

在 Razor Pages、MVC、SignalR 和 Web API 应用中,使用 AddContext 方法 JsonSerializerOptions,如以下示例所示:

[JsonSerializable(typeof(WeatherForecast[]))]
internal partial class MyJsonContext : JsonSerializerContext { }
services.AddControllers().AddJsonOptions(options =>
    options.JsonSerializerOptions.AddContext<MyJsonContext>());

注意

异步序列化不支持 JsonSourceGenerationMode.Serialization(快速路径序列化)。

在 .NET 7 及更早版本中,此限制也适用于接受 StreamJsonSerializer.Serialize 的同步重载。 从 .NET 8 开始,即使流式处理序列化需要基于元数据的模型,但如果已知有效负载足够小,预定的缓冲区大小足够容纳,则它将回退到快速路径。 有关详细信息,请参阅 https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/#json

禁用反射默认值

由于 System.Text.Json 默认使用反射,因此调用基本序列化方法可能会中断本机 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 中运行的应用程序中对基于反射的解析程序进行 root 操作。

指定选项

在 .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 指定序列化选项时,调用以下一种序列化方法:

  • 采用 TypeInfo<TValue>JsonSerializer.Serialize 方法。 将上下文类的 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 实例的构造函数来创建和使用上下文实例,则会使用提供的实例,而不是 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 指定选项

无法使用 JsonSourceGenerationOptionsAttribute 设置 JsonSerializerOptions 的某些选项。 若要通过使用 JsonSerializerOptions 指定选项:

  • 创建 JsonSerializerOptions 的实例。
  • 创建派生自 JsonSerializerContext 的类的实例,并将 JsonSerializerOptions 实例传递给构造函数。
  • 调用 JsonSerializer 的序列化或反序列化方法(采用上下文实例或 TypeInfo<TValue>)。

下面是一个示例上下文类,后跟序列化和反序列化示例代码:

[JsonSerializable(typeof(WeatherForecast))]
internal partial class OptionsExampleContext : JsonSerializerContext
{
}
jsonString = JsonSerializer.Serialize(
    weatherForecast,
    typeof(WeatherForecast),
    new OptionsExampleContext(
        new JsonSerializerOptions(JsonSerializerDefaults.Web)));
weatherForecast = JsonSerializer.Deserialize(
    jsonString, 
    typeof(WeatherForecast), 
    new OptionsExampleContext(
        new JsonSerializerOptions(JsonSerializerDefaults.Web)))
        as WeatherForecast;

下面是上述示例在完整程序中的情况:

using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonSerializerOptionsExample
{
    public class WeatherForecast
    {
        public DateTime Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }
    }

    [JsonSerializable(typeof(WeatherForecast))]
    internal partial class OptionsExampleContext : 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, 
                typeof(WeatherForecast), 
                new OptionsExampleContext(
                    new JsonSerializerOptions(JsonSerializerDefaults.Web)))
                    as WeatherForecast;
            Console.WriteLine($"Date={weatherForecast?.Date}");
            // output:
            //Date=8/1/2019 12:00:00 AM

            jsonString = JsonSerializer.Serialize(
                weatherForecast,
                typeof(WeatherForecast),
                new OptionsExampleContext(
                    new JsonSerializerOptions(JsonSerializerDefaults.Web)));
            Console.WriteLine(jsonString);
            // output:
            //{ "date":"2019-08-01T00:00:00","temperatureCelsius":25,"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 按其指定顺序查询每个解析程序,并返回非 null 的第一个结果。

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 类型。)

使用 JsonConverterAttribute 特性,通过 JsonStringEnumConverter<TEnum> 转换器对枚举类型进行批注:

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"
}

一揽子策略

可以使用 JsonSourceGenerationOptionsAttribute 应用一揽子策略来将枚举序列化为字符串,而不是使用 JsonStringEnumConverter<TEnum> 类型。 创建 JsonSerializerContext 类,并使用 JsonSerializableAttributeJsonSourceGenerationOptionsAttribute 特性对其进行注释:

[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
}

另请参阅