CodeGym /课程 /C# SELF /序列化设置 ( JsonSerializerOptio...

序列化设置 ( JsonSerializerOptions)

C# SELF
第 45 级, 课程 3
可用

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,可能会出问题,比如名字大小写或格式不一致。

2
任务
C# SELF, 第 45 级, 课程 3
已锁定
设置 JSON 中日期格式
设置 JSON 中日期格式
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION