1. 介绍
如果你的需求刚好符合默认行为,那就棒极了。但很多时候客户(或第三方后端)会要求严格的格式:到处都是 camelCase,日期 — ISO 8601,跳过 null,自定义转换器,还有各种集成器的鬼点子。
这时我们的老朋友——类 JsonSerializerOptions 上场了。
如果把序列化比作厨房,那这个类就是你的调料、锅具和秘密配方,可以把序列化调成你想要的口味。
如何使用 JsonSerializerOptions
要定制序列化或反序列化过程,只需把配置对象传给序列化器的方法:
using System.Text.Json;
var options = new JsonSerializerOptions
{
// 这里写你的设置
};
string json = JsonSerializer.Serialize(myObject, options);
var obj = JsonSerializer.Deserialize<MyType>(json, options);
这个模式非常常见:你创建一个 JsonSerializerOptions,按需配置(见下),然后传给序列化器的 Serialize/Deserialize 方法。
注意:同一个 JsonSerializerOptions 对象完全可以(而且建议)在多次序列化/反序列化中复用。
2. JsonSerializerOptions 的主要设置
来看看这个类里最常用的“旋钮”和“开关”。
JSON 格式化 — WriteIndented
讨厌单行的 JSON 看着扎眼?别急 —— 打开漂亮的缩进!
var options = new JsonSerializerOptions
{
WriteIndented = true // 漂亮的「可读」JSON
};
var person = new Person { Name = "Anna", Age = 30 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json);
输出:
{
"Name": "Anna",
"Age": 30
}
如果不设置 WriteIndented = true,你会得到压缩的一行形式 {"Name":"Anna","Age":30}。生产环境通常用“最小化”JSON(体积小),而调试或日志记录时用格式化的更方便看。
命名风格 — PropertyNamingPolicy
很多 API 要求属性名是 camelCase(比如用 firstName 而不是 FirstName)。在 .NET 里属性习惯上用 PascalCase,这时就用 PropertyNamingPolicy:
var options = new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
var person = new Person { Name = "Oleg", Age = 25 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json); // {"name":"Oleg","age":25}
如果需要自定义风格(比如全部大写),可以实现自己的 JsonNamingPolicy。大多数情况 CamelCase 就够用了。
3. 忽略属性
是否跳过 null 值 — DefaultIgnoreCondition
有时候你希望值为 null 的字段完全不出现在 JSON 里。原因可能是想让 JSON 更紧凑,或者外部系统不接受 null。
为此使用属性 DefaultIgnoreCondition:
using System.Text.Json.Serialization;
var options = new JsonSerializerOptions
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
var person = new Person { Name = null, Age = 22 };
string json = JsonSerializer.Serialize(person, options);
Console.WriteLine(json); // {"Age":22}
如果你想不仅跳过 null,还想跳过默认值(比如 int=0),用 WhenWritingDefault:
options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault;
序列化 fields(包括私有) — IncludeFields 和 [JsonInclude]
默认只会序列化有公共 setter 的公共属性。如果想把字段(fields)也序列化,可以打开:
var options = new JsonSerializerOptions
{
IncludeFields = true
};
示例类:
public class Item
{
public string Name;
public int Id { get; set; }
}
var item = new Item { Name = "Book", Id = 12 };
string json = JsonSerializer.Serialize(item, options);
// {"Name":"Book","Id":12}
如果需要序列化私有字段/属性,可以使用特性 [JsonInclude],但那是另一讲的内容。
4. 值转换
日期格式化 — Converters
默认情况下 System.Text.Json 会把日期序列化成严格的 ISO 8601(例如 "2023-12-27T15:30:45.123Z"),这通常很方便。但有时后端要特别的日期格式。
这时可以挂自定义的 converter。下面是个基础示例:
using System.Text.Json;
using System.Text.Json.Serialization;
public class CustomDateTimeConverter : JsonConverter<DateTime>
{
private string _format = "yyyyMMdd";
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var date = reader.GetString();
return DateTime.ParseExact(date, _format, null);
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString(_format));
}
}
// 使用 converter
var options = new JsonSerializerOptions();
options.Converters.Add(new CustomDateTimeConverter());
var dateObj = new { Date = new DateTime(2022, 1, 5) };
string json = JsonSerializer.Serialize(dateObj, options); // {"Date":"20220105"}
5. 有用的细节
反序列化时忽略属性大小写 — PropertyNameCaseInsensitive
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
var json = "{\"name\":\"Vasya\",\"age\":33}";
var person = JsonSerializer.Deserialize<Person>(json, options);
// person.Name == "Vasya"
控制嵌套和最大对象深度 — MaxDepth
var options = new JsonSerializerOptions
{
MaxDepth = 32 // 默认是 64
};
但如果你的对象可能无限嵌套,比如某种双向引用的树结构,最好从设计上优化模型。
JSON 中的注释 — ReadCommentHandling(反序列化)
JSON 标准不支持注释,但有时会遇到带 // 注释的“创意”文件。
var options = new JsonSerializerOptions
{
ReadCommentHandling = JsonCommentHandling.Skip
};
string json = "{\n \"Name\": \"Ivan\", // 用户名\n \"Age\": 30\n}";
var person = JsonSerializer.Deserialize<Person>(json, options);
特殊字符支持 — Encoder
有时候你需要控制序列化器如何转义字符(比如不想把西里尔字符转成 \uXXXX)。可以指定自己的 encoder:
using System.Text.Encodings.Web;
var options = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};
小心使用,否则你的 emoji/汉字可能会意外被转成 Unicode 序列!
6. 序列化设置常见错误
错误 №1:忘记公共 setter。 属性不会被序列化,因为它没有公共的 setter,或者是私有的。
错误 №2:错误地忽略了 null 值。 打开了 DefaultIgnoreCondition 后,null 属性不会出现在 JSON 中,但你可能没意识到这一点。
错误 №3:日期格式不对。 日期没有按预期序列化 —— 应该挂自定义转换器(JsonConverter)。
错误 №4:期望字段顺序固定。 JSON 标准不保证字段顺序。如果 API 要求严格顺序,你要么改模型,要么用第三方库来实现。
错误 №5:序列化和反序列化用的不一样的配置。 如果写和读时没有用同样的 JsonSerializerOptions,可能会出问题,比如名字大小写或格式不一致。
GO TO FULL VERSION