如何使用 System.Text.Json 自定义属性名称和值

默认情况下,属性名称和字典键在 JSON 输出中保持不变,包括大小写。 枚举值表示为数字。 属性按定义的顺序进行序列化。 但是,可以通过以下方法自定义这些行为:

  • 指定特定的序列化属性名称。
  • 使用内置命名策略(如 camelCase、snake_case 或 kebab-case)作为属性名称和字典键。
  • 对属性名称和字典键使用自定义命名策略。
  • 使用或不使用命名策略序列化枚举值作为字符串。
  • 配置序列化属性的顺序。

注意

Web 默认命名策略为 camel case。

对于需要对 JSON 属性名称和值进行特殊处理的其他方案,可以实现自定义转换器

自定义单个属性名称

若要设置单个属性的名称,请使用 [JsonPropertyName] 特性。

下面是要进行序列化的示例类型和生成的 JSON:

public class WeatherForecastWithPropertyNameAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
Public Class WeatherForecastWithPropertyNameAttribute
    Public Property [Date] As DateTimeOffset
    Public Property TemperatureCelsius As Integer
    Public Property Summary As String

    <JsonPropertyName("Wind")>
    Public Property WindSpeed As Integer

End Class
{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "Wind": 35
}

此特性设置的属性名称:

使用内置命名策略

下表显示了内置命名策略以及它们如何影响属性名称。

命名策略 说明 原始属性名称 经过转换的属性名称
CamelCase 第一个单词以小写字符开头。
连续单词以大写字符开头。
TempCelsius tempCelsius
KebabCaseLower* 单词由连字符分隔。
所有字符均为小写。
TempCelsius temp-celsius
KebabCaseUpper* 单词由连字符分隔。
所有字符均为大写。
TempCelsius TEMP-CELSIUS
SnakeCaseLower* 单词用下划线分隔。
所有字符均为小写。
TempCelsius temp_celsius
SnakeCaseUpper* 单词用下划线分隔。
所有字符均为大写。
TempCelsius TEMP_CELSIUS

* 在 .NET 8 及更高版本中可用。

以下示例演示如何通过将 JsonSerializerOptions.PropertyNamingPolicy 设置为 JsonNamingPolicy.CamelCase,对所有 JSON 属性名称使用 camel case:

var serializeOptions = new JsonSerializerOptions
{
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);
Dim serializeOptions As JsonSerializerOptions = New JsonSerializerOptions With {
    .PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions)

下面是要进行序列化的示例类和 JSON 输出:

public class WeatherForecastWithPropertyNameAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
Public Class WeatherForecastWithPropertyNameAttribute
    Public Property [Date] As DateTimeOffset
    Public Property TemperatureCelsius As Integer
    Public Property Summary As String

    <JsonPropertyName("Wind")>
    Public Property WindSpeed As Integer

End Class
{
  "date": "2019-08-01T00:00:00-07:00",
  "temperatureCelsius": 25,
  "summary": "Hot",
  "Wind": 35
}

命名策略:

  • 适用于序列化和反序列化。
  • [JsonPropertyName] 特性替代。 这便是示例中的 JSON 属性名称 Wind 不是 camel 大小写的原因。

注意

内置命名策略都不支持代理项对的字母。 有关详细信息,请参阅 dotnet/runtime 问题 90352

使用自定义 JSON 属性命名策略

若要使用自定义 JSON 属性命名策略,请创建派生自 JsonNamingPolicy 的类,并替代 ConvertName 方法,如下面的示例中所示:

using System.Text.Json;

namespace SystemTextJsonSamples
{
    public class UpperCaseNamingPolicy : JsonNamingPolicy
    {
        public override string ConvertName(string name) =>
            name.ToUpper();
    }
}
Imports System.Text.Json

Namespace SystemTextJsonSamples

    Public Class UpperCaseNamingPolicy
        Inherits JsonNamingPolicy

        Public Overrides Function ConvertName(name As String) As String
            Return name.ToUpper()
        End Function

    End Class

End Namespace

然后,将 JsonSerializerOptions.PropertyNamingPolicy 属性设置为命名策略类的实例:

var options = new JsonSerializerOptions
{
    PropertyNamingPolicy = new UpperCaseNamingPolicy(),
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);
Dim options As JsonSerializerOptions = New JsonSerializerOptions With {
    .PropertyNamingPolicy = New UpperCaseNamingPolicy,
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast1, options)

下面是要进行序列化的示例类和 JSON 输出:

public class WeatherForecastWithPropertyNameAttribute
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string? Summary { get; set; }
    [JsonPropertyName("Wind")]
    public int WindSpeed { get; set; }
}
Public Class WeatherForecastWithPropertyNameAttribute
    Public Property [Date] As DateTimeOffset
    Public Property TemperatureCelsius As Integer
    Public Property Summary As String

    <JsonPropertyName("Wind")>
    Public Property WindSpeed As Integer

End Class
{
  "DATE": "2019-08-01T00:00:00-07:00",
  "TEMPERATURECELSIUS": 25,
  "SUMMARY": "Hot",
  "Wind": 35
}

JSON 属性命名策略:

  • 适用于序列化和反序列化。
  • [JsonPropertyName] 特性替代。 这便是示例中的 JSON 属性名称 Wind 不是大写的原因。

对字典键使用命名策略

如果要序列化的对象的属性的类型为 Dictionary<string,TValue>,则可以使用命名策略(如 camel case)转换 string 键。 为此,请将 JsonSerializerOptions.DictionaryKeyPolicy 设置为所需的命名策略。 以下示例使用 CamelCase 命名策略:

var options = new JsonSerializerOptions
{
    DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
    WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);
Dim options As JsonSerializerOptions = New JsonSerializerOptions With {
    .DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
    .WriteIndented = True
}
jsonString = JsonSerializer.Serialize(weatherForecast, options)

使用名为 TemperatureRanges 且具有键值对 "ColdMinTemp", 20"HotMinTemp", 40 的字典序列化对象会产生类似于以下示例的 JSON 输出:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "Hot",
  "TemperatureRanges": {
    "coldMinTemp": 20,
    "hotMinTemp": 40
  }
}

字典键的命名策略仅适用于序列化。 如果反序列化字典,即使将 JsonSerializerOptions.DictionaryKeyPolicy 设置为非默认命名策略,这些键也会与 JSON 文件匹配。

作为字符串的枚举

默认情况下,枚举会序列化为数字。 若要将枚举名称序列化为字符串,请使用 JsonStringEnumConverterJsonStringEnumConverter<TEnum> 转换器。 Native AOT 运行时仅支持 JsonStringEnumConverter<TEnum>

默认情况下,枚举会序列化为数字。 若要将枚举名称序列化为字符串,请使用 JsonStringEnumConverter 转换器。

例如,假设需要序列化以下具有枚举的类:

public class WeatherForecastWithEnum
{
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public Summary? Summary { get; set; }
}

public enum Summary
{
    Cold, Cool, Warm, Hot
}
Public Class WeatherForecastWithEnum
    Public Property [Date] As DateTimeOffset
    Public Property TemperatureCelsius As Integer
    Public Property Summary As Summary
End Class

Public Enum Summary
    Cold
    Cool
    Warm
    Hot
End Enum

如果 Summary 为 Hot,则默认情况下序列化的 JSON 具有数值 3:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": 3
}

下面的示例代码序列化枚举名称(而不是数值),并将名称转换为 camel 大小写:

options = new JsonSerializerOptions
{
    WriteIndented = true,
    Converters =
    {
        new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
    }
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);
options = New JsonSerializerOptions With {
    .WriteIndented = True
}
options.Converters.Add(New JsonStringEnumConverter(JsonNamingPolicy.CamelCase))
jsonString = JsonSerializer.Serialize(weatherForecast, options)

生成的 JSON 类似于以下示例:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Summary": "hot"
}

内置 JsonStringEnumConverter 还可以反序列化字符串值。 无论有没有指定的命名策略,它都能正常工作。 下面的示例演示如何使用 CamelCase 进行反序列化:

options = new JsonSerializerOptions
{
    Converters =
    {
        new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
    }
};
weatherForecast = JsonSerializer.Deserialize<WeatherForecastWithEnum>(jsonString, options)!;
options = New JsonSerializerOptions
options.Converters.Add(New JsonStringEnumConverter(JsonNamingPolicy.CamelCase))
weatherForecast = JsonSerializer.Deserialize(Of WeatherForecastWithEnum)(jsonString, options)

还可以通过用 JsonConverterAttribute 批注枚举来指定要使用的转换器。 以下示例演示如何使用 JsonConverterAttribute 属性来指定 JsonStringEnumConverter<TEnum>(.NET 8 和更高版本中可用)。 例如,假设需要序列化以下具有枚举的类:

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
}

以下示例代码序列化枚举名称,而不是数值:

var options = new JsonSerializerOptions
{
    WriteIndented = true,
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

生成的 JSON 类似于以下示例:

{
  "Date": "2019-08-01T00:00:00-07:00",
  "TemperatureCelsius": 25,
  "Precipitation": "Sleet"
}

若要将转换器用于源生成,请参阅将枚举字段序列化为字符串

配置序列化属性的顺序

默认情况下,属性按在类中定义的顺序进行序列化。 通过 [JsonPropertyOrder] 特性,可指定序列化的 JSON 输出中的属性顺序。 Order 属性的默认值是零。 如果将 Order 设置为正数,则会将属性放置在具有默认值的属性后面。 如果 Order 是负数,则会将属性放置在具有默认值的属性前面。 属性按 Order 值从小到大的顺序编写的。 下面是一个示例:

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

namespace PropertyOrder
{
    public class WeatherForecast
    {
        [JsonPropertyOrder(-5)]
        public DateTime Date { get; set; }
        public int TemperatureC { get; set; }
        [JsonPropertyOrder(-2)]
        public int TemperatureF { get; set; }
        [JsonPropertyOrder(5)]
        public string? Summary { get; set; }
        [JsonPropertyOrder(2)]
        public int WindSpeed { get; set; }
    }

    public class Program
    {
        public static void Main()
        {
            var weatherForecast = new WeatherForecast
            {
                Date = DateTime.Parse("2019-08-01"),
                TemperatureC = 25,
                TemperatureF = 25,
                Summary = "Hot",
                WindSpeed = 10
            };

            var options = new JsonSerializerOptions { WriteIndented = true };
            string jsonString = JsonSerializer.Serialize(weatherForecast, options);
            Console.WriteLine(jsonString);
        }
    }
}
// output:
//{
//  "Date": "2019-08-01T00:00:00",
//  "TemperatureF": 25,
//  "TemperatureC": 25,
//  "WindSpeed": 10,
//  "Summary": "Hot"
//}

请参阅