总目录
前言
在 C# 开发中,System.Text.Json
是一个高性能的 JSON 处理库,广泛用于序列化和反序列化对象。当默认的序列化行为无法满足需求时,JsonConverter
提供了强大的自定义能力。本文将详细讲解 JsonConverter
的使用方法,帮助你灵活处理复杂的 JSON 数据。
一、 JsonConverter 是什么?
1. 概述
JsonConverter
是 System.Text.Json.Serialization
命名空间中的一个抽象类,它提供了将对象或值转换为 JSON 格式以及将 JSON 格式转换回对象或值的功能。System.Text.Json 提供了内置的 JsonConverter 实现,用于处理大多数基本类型,同时开发者也可以通过实现 JsonConverter<T>
创建自定义的 JsonConverter 来满足特定需求。
JsonConverter
是 System.Text.Json 中用于 自定义序列化和反序列化逻辑的核心类。它允许开发者完全控制 .NET 对象与 JSON 格式之间的转换过程,
2. 为什么需要 JsonConverter?
默认的序列化行为可能无法满足以下需求:
- 特殊数据格式:日期、货币、自定义对象,如日期需格式化为
yyyy-MM-dd HH:mm:ss
。 - 非标准 JSON 结构:如将嵌套对象转换为扁平化 JSON。
- 安全处理:过滤敏感字段或转换加密数据。
- 性能优化:避免反射或减少内存分配。
通过继承 JsonConverter<T>
,开发者可以完全控制类型与 JSON 之间的转换过程。
3. JsonConverter 核心原理
JsonConverter<T>
是一个泛型类,要求实现以下两个关键方法:
Read
:从Utf8JsonReader
中读取 JSON 数据并转换为 .NET 对象。Write
:将 .NET 对象写入Utf8JsonWriter
生成 JSON。
代码示例:
public class CustomConverter : JsonConverter<CustomType>
{public override CustomType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){// 实现反序列化逻辑}public override void Write(Utf8JsonWriter writer, CustomType value, JsonSerializerOptions options){// 实现序列化逻辑}
}
二、使用
1. 内置 JsonConverter
1)内置 JsonConverter
介绍
▶ 内置转换器
System.Text.Json 为映射到 JavaScript 基元的大多数基本类型提供了内置转换器。这些内置转换器可以处理以下基本类型:字符串、整数、浮点数、布尔值、数组和集合、字典、日期和时间(ISO 8601 格式)。
对于日期和时间类型,System.Text.Json 实现了 ISO 8601-1:2019 扩展配置文件,定义了日期和时间表示形式的组件。
详见:System.Text.Json 中的 DateTime 和 DateTimeOffset 支持。
▶ 使用内置 JsonConverter
System.Text.Json 提供的内置转换器可以处理大多数常见类型,无需额外配置即可使用。以下是一个简单的使用示例:
public class Person
{public string Name { get; set; }public int Age { get; set; }public DateTime BirthDate { get; set; }
}
class Program
{static void Main(){// 使用内置转换器序列化对象var person = new Person{Name = "John Doe",Age = 30,BirthDate = new DateTime(1990, 1, 1)};string json = JsonSerializer.Serialize(person);Console.WriteLine(json);// 输出:{"Name":"John Doe","Age":30,"BirthDate":"1990-01-01T00:00:00"}}
}
在上述示例中,System.Text.Json 使用内置转换器自动处理了 Person 对象的所有属性,包括 DateTime 类型的 BirthDate 属性,以 ISO 8601 格式进行序列化。
2)内置转换器:JsonStringEnumConverter
JsonStringEnumConverter
是 System.Text.Json
中用于 将枚举(Enum
)类型序列化为字符串,并支持从字符串反序列化为枚举值的内置转换器。它是 JsonConverter
的一个特化实现,专门处理枚举类型,解决了默认序列化(将枚举值转为整数)的局限性。
▶ 通过JsonSerializerOptions
全局使用
定义枚举类型和相关对象:
public enum Status { Active, Inactive, Pending }
public enum CountryType { China, USA,Japan }public class Order
{public int Id { get; set; }public string Name { get; set; }public CountryType CountryType { get; set; }public Status Status { get; set; }
}
默认枚举会序列化为整数,使用 JsonStringEnumConverter
可转为字符串:
class Program
{static void Main(){// 正常序列化var order = new Order { Id = 1, Name = "Order0001", CountryType = CountryType.China, Status = Status.Pending };string json = JsonSerializer.Serialize(order);Console.WriteLine(json);// 输出:{"Id":1,"Name":"Order0001","CountryType":0,"Status":2}// 方式1var options = new JsonSerializerOptions{Converters = { new JsonStringEnumConverter() }};json = JsonSerializer.Serialize(order, options);Console.WriteLine(json);// 输出:{"Id":1,"Name":"Order0001","CountryType":"China","Status":"Pending"}// 方式2var options2 = new JsonSerializerOptions();options2.Converters.Add(new JsonStringEnumConverter());json = JsonSerializer.Serialize(order, options);Console.WriteLine(json);// 输出:{"Id":1,"Name":"Order0001","CountryType":"China","Status":"Pending"}}
}
▶ 针对特定枚举类型
如下例中通过JsonSerializerOptions
中的Converters
配置只针对 Status
枚举的转换
var options = new JsonSerializerOptions
{Converters = { new JsonStringEnumConverter<Status>() }
};
json = JsonSerializer.Serialize(order, options);
Console.WriteLine(json);
// 输出:{"Id":1,"Name":"Order0001","CountryType":0,"Status":"Pending"}
▶ 通过JsonConverter
特性使用
直接在枚举类型上使用 [JsonConverter] 特性,适用于指定特定属性使用
// 第一种方式:直接在枚举上添加特性
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum Status{ Active, Inactive, Pending}
public enum CountryType{China, USA,Japan}public class Order
{public int Id { get; set; }public string Name { get; set; }// 第二种方式:在对象相关属性上添加特性[JsonConverter(typeof(JsonStringEnumConverter))]public CountryType CountryType { get; set; }public Status Status { get; set; }
}
class Program
{static void Main(){// 正常序列化var order = new Order { Id = 1, Name = "Order0001", CountryType = CountryType.China, Status = Status.Pending };string json = JsonSerializer.Serialize(order);Console.WriteLine(json);// 输出:{"Id":1,"Name":"Order0001","CountryType":"China","Status":"Pending"}}
}
▶ 配置JsonStringEnumConverter 的命名策略
可通过 JsonNamingPolicy
自定义枚举值的输出格式(如 CamelCase、PascalCase 等)。
public enum Status{ Active, Inactive, Pending}
public enum CountryType{China, USA,Japan}public class Order
{public int Id { get; set; }public string Name { get; set; } public CountryType CountryType { get; set; }public Status Status { get; set; }
}
class Program
{static void Main(){// 正常序列化var order = new Order { Id = 1, Name = "Order0001", CountryType = CountryType.China, Status = Status.Pending };var options = new JsonSerializerOptions() { Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) }};string json = JsonSerializer.Serialize(order,options);Console.WriteLine(json);// 输出:{"Id":1,"Name":"Order0001","CountryType":"china","Status":"pending"}}
}
2. 自定义 JsonConverter
当内置转换器无法满足需求时,开发者可以创建自定义的 JsonConverter。自定义转换器需要继承 JsonConverter 类并实现两个主要方法:Read 和 Write。
下面 将通过 将日期序列化为 yyyy-MM-dd
的案例来说明如何自定义 JsonConverter
1)创建自定义转换器
创建自定义转换器类,继承 JsonConverter<T>
。
public class DateFormatterConverter : JsonConverter<DateTime>
{public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){// 实现 反序列化逻辑:将Json 转化为 .NET对象}public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options){// 实现 序列化逻辑:将 .NET对象 转化为Json}
}
2)实现 Read
和 Write
方法
public class DateFormatterConverter : JsonConverter<DateTime>
{public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){return DateTime.Parse(reader.GetString()!);}public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options){writer.WriteStringValue(value.ToString("yyyy-MM-dd"));}
}
3)使用自定义JsonConverter
▶ 通过JsonSerializerOptions
全局使用
public class Person
{public string Name { get; set; }public int Age { get; set; }public DateTime BirthDate { get; set; }
}class Program
{static void Main(){var person = new Person{Name = "John Doe",Age = 30,BirthDate = new DateTime(1990, 1, 1)};var options = new JsonSerializerOptions() { Converters = { new DateFormatterConverter() }};string json = JsonSerializer.Serialize(person, options);Console.WriteLine(json);// 输出:{"Name":"John Doe","Age":30,"BirthDate":"1990-01-01"}}
}
▶ 通过JsonConverter
特性使用
public class Person
{public string Name { get; set; }public int Age { get; set; }[JsonConverter(typeof(DateFormatterConverter))]public DateTime BirthDate { get; set; }
}class Program
{static void Main(){var person = new Person{Name = "John Doe",Age = 30,BirthDate = new DateTime(1990, 1, 1)};string json = JsonSerializer.Serialize(person);Console.WriteLine(json);// 输出:{"Name":"John Doe","Age":30,"BirthDate":"1990-01-01"}}
}
三、实战案例
1. 自定义日期格式
1)创建自定义转换器类
System.Text.Json 默认使用 ISO 8601 格式序列化日期。如果需要自定义日期格式,可以创建一个处理 DateTime 的自定义转换器:
public class CustomDateTimeConverter : JsonConverter<DateTime>
{private readonly string _format;public CustomDateTimeConverter(string format){_format = format;}public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){if (reader.TokenType == JsonTokenType.String){return DateTime.ParseExact(reader.GetString()!, _format, System.Globalization.CultureInfo.InvariantCulture);}throw new JsonException();}public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options){writer.WriteStringValue(value.ToString(_format));}
}
2)使用自定义转换器
class Program
{static void Main(){// 使用自定义日期格式var options = new JsonSerializerOptions{Converters = { new CustomDateTimeConverter("yyyy-MM-dd") }};var person = new{Name = "John Doe",Age = 30,BirthDate = new DateTime(1990, 1, 1)};string json = JsonSerializer.Serialize(person,options);Console.WriteLine(json);// 输出:{"Name":"John Doe","Age":30,"BirthDate":"1990-01-01"}var options2 = new JsonSerializerOptions();options2.Converters.Add(new CustomDateTimeConverter("yyyy-MM-dd HH:mm:ss"));json = JsonSerializer.Serialize(person,options2);Console.WriteLine(json);// 输出:{"Name":"John Doe","Age":30,"BirthDate":"1990-01-01 00:00:00"}}
}
在这个示例中,StringEnumConverter 用于将 DayOfWeek 枚举转换为字符串。Read 方法将 JSON 字符串解析为 DayOfWeek 枚举值,Write 方法将 DayOfWeek 枚举值写为 JSON 字符串。
2. 自定义枚举格式
如果需要自定义枚举的序列化行为,可以创建自己的转换器:
public class CustomEnumConverter : JsonConverter<DayOfWeek>
{public override DayOfWeek Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options){string dayString = reader.GetString();if (Enum.TryParse(dayString, out DayOfWeek day)){return day;}throw new JsonException("Invalid day of week.");}public override void Write(Utf8JsonWriter writer, DayOfWeek value, JsonSerializerOptions options){writer.WriteStringValue(value.ToString());}
}
3. 将对象转换为扁平化 JSON
需求:将嵌套对象扁平化为 JSON。
public class Address
{public string City { get; set; }public string ZipCode { get; set; }
}public class User
{public string Name { get; set; }public Address Address { get; set; }
}// 自定义转换器
public class UserConverter : JsonConverter<User>
{public override User Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options){var user = new User();while (reader.Read()){if (reader.TokenType == JsonTokenType.EndObject)return user;if (reader.TokenType == JsonTokenType.PropertyName){var propName = reader.GetString();reader.Read();switch (propName.ToLower()){case "name":user.Name = reader.GetString();break;case "city":user.Address ??= new Address();user.Address.City = reader.GetString();break;case "zip":user.Address ??= new Address();user.Address.ZipCode = reader.GetString();break;}}}throw new JsonException("Unexpected JSON structure");}public override void Write(Utf8JsonWriter writer, User user, JsonSerializerOptions options){writer.WriteStartObject();writer.WriteString("name", user.Name);if (user.Address != null){writer.WriteString("city", user.Address.City);writer.WriteString("zip", user.Address.ZipCode);}writer.WriteEndObject();}
}
使用示例:
class Program
{static void Main(){var user = new User { Name = "Alice", Address = new Address { City = "Beijing", ZipCode = "100000" } };// 序列化string json = JsonSerializer.Serialize(user); Console.WriteLine(json);// 输出:{"Name":"Alice","Address":{"City":"Beijing","ZipCode":"100000"}}// 序列化:扁平化JSON var options = new JsonSerializerOptions { Converters = { new UserConverter() } };json = JsonSerializer.Serialize(user, options); Console.WriteLine(json);// 输出:{"name":"Alice","city":"Beijing","zip":"100000"}// 反序列化:扁平化JSON string jsonString = """{"name":"Alice","city":"Beijing","zip":"100000"}""";var user2= JsonSerializer.Deserialize<User>(jsonString,options);Console.WriteLine($"Name = {user2.Name} , City = {user2.Address.City} , ZipCode = {user2.Address.ZipCode}");// 输出:Name = Alice , City = Beijing , ZipCode = 100000}
}
4. 过滤敏感字段
需求:序列化时忽略敏感字段(如密码)。
public class User
{public string Name { get; set; }public string Password { get; set; } // 需要忽略的字段
}public class SecureUserConverter : JsonConverter<User>
{public override User Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options){// 反序列化逻辑(略)throw new NotImplementedException();}public override void Write(Utf8JsonWriter writer, User user, JsonSerializerOptions options){writer.WriteStartObject();writer.WriteString("name", user.Name);// 跳过 Password 字段writer.WriteEndObject();}
}
📌 关于以上案例中,自定义JsonConverter 里的Read 和 Write 方法所涉及的Utf8JsonReader 与Utf8JsonWriter 对象的详细内容,可见:C# Utf8JsonReader 和 Utf8JsonWriter 使用详解
四、性能优化技巧
1. 避免反射
直接操作 Utf8JsonReader
和 Utf8JsonWriter
,减少对象创建:
public override void Write(Utf8JsonWriter writer, MyType value, JsonSerializerOptions options)
{writer.WriteStartObject();writer.WriteString("key", value.Property); // 直接写入属性writer.WriteEndObject();
}
2. 缓存转换器实例
private static readonly DateFormatterConverter _dateConverter = new DateFormatterConverter();
var options = new JsonSerializerOptions { Converters = { _dateConverter } };
五、异常处理与调试
1. 捕获反序列化错误
public override User Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
{try{// 反序列化逻辑}catch (Exception ex){throw new JsonException("Failed to parse user data", ex);}
}
2. 日志记录与调试
在 Read
或 Write
方法中添加日志输出:
Console.WriteLine($"Serializing {value.Name} to JSON");
结语
回到目录页:C#/.NET 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料:
- 如何在 .NET 中编写用于 JSON 序列化(封送)的自定义转换器
- .NET 官方文档:System.Text.Json
- JsonConverter 源码分析