如何使用 System.Text.Json 自定义属性名称和值
默认情况下,属性名称和字典键在 JSON 输出中保持不变,包括大小写。 枚举值表示为数字。 属性按定义的顺序进行序列化。 但是,可以通过以下方法自定义这些行为:
- 指定特定的序列化属性名称。
- 使用内置命名策略(如 camelCase、snake_case 或 kebab-case)作为属性名称和字典键。
- 对属性名称和字典键使用自定义命名策略。
- 使用或不使用命名策略序列化枚举值作为字符串。
- 配置序列化属性的顺序。
注意
Web 默认命名策略为 camel case。
对于需要对 JSON 属性名称和值进行特殊处理的其他方案,可以实现自定义转换器。
自定义单个属性名称
若要设置单个属性的名称,请使用 [JsonPropertyName] 特性。
下面是要进行序列化的示例类型和生成的 JSON:
public class WeatherForecastWithPropertyName
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}
Public Class WeatherForecastWithPropertyName
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 WeatherForecastWithPropertyName
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}
Public Class WeatherForecastWithPropertyName
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 WeatherForecastWithPropertyName
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}
Public Class WeatherForecastWithPropertyName
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 文件匹配。
作为字符串的枚举
默认情况下,枚举会序列化为数字。 若要将枚举名称序列化为字符串,请使用 JsonStringEnumConverter 或 JsonStringEnumConverter<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"
//}