.NET中自定义JSON转换器的实战指南
目录
- 一、为什么需要自定义 jsON 转换器?
- 二、基本模式:实现 JsonConverter<T>
- 2.1 适用场景
- 2.2 核心方法
- 2.3 实战案例:日期格式自定义转换器
- 场景需求
- 使用示例
- 技术细节
- 三、工厂模式:处理开放式泛型类型
- 3.1 适用场景
- 3.2 核心方法
- 3.3 实战案例:泛型集合转换器
- 场景需求
- 代码实现
- 使用示例
- 技术细节
- 四、性能优化技巧
- 4.1 避免频繁创建对象
- 4.2 使用高性能 API
- 4.3 内存优化
- 五、高级场景:科学计数法转字符串
- 5.1 问题描述
- 5.2 解决方案
- 绑定模型
- 六、常见陷阱与解决方案
- 6.1 CanConvert 方法误判
- 6.2 嵌套类型处理失败
- 6.3 线程安全问题
- 七、自定义转换器的设计哲学
- 代码模板
- 基本模式模板
- 工厂模式模板
一、为什么需要自定义 JSON 转换器?
在 .NET 开发中,JSON 序列化与反序列化是日常开发的核心操作。然而,标准的序列化器(如 Sysjavascripttem.Text.Json
)并不能满足所有场景需求。以下是常见的痛点:
- 数据格式不兼容
- 例如:前端要求日期格式为
"yyyy-MM-dd"
,而默认格式为 ISO 8601。
- 例如:前端要求日期格式为
- 复杂类型处理
- 例如:枚举类型需要自定义映射规则(如
Enum
转string
而非int
)。
- 例如:枚举类型需要自定义映射规则(如
- 性能瓶颈
- 例如:需要避免频繁创建临时对象或优化内存分配。
- 特殊业务逻辑
- 例如:将数据库中的科学计数法字段(
1.23E+08
)转为正常数字格式。
- 例如:将数据库中的科学计数法字段(
自定义 JSON 转换器(JsonConverter<T>
)正是解决这些问题的利器。本文将通过 基本模式 和 工厂模式 的实战案例,带你掌握自定义转换器的核心技巧。
二、基本模式:实现 JsonConverter<T>
2.1 适用场景
- 非泛型类型 或 封闭式泛型类型(如
List<DateTime>
、Dictionary<string, int>
)。 - 需要控制特定类型的序列化/反序列化逻辑。
2.2 核心方法
Read
:将 JSON 反序列化为 .NET 对象。Write
:将 .NET 对象序列化为 JSON。CanConvert
(可选):判断是否支持该类型。
2.3 实战案例:日期格式自定义转换器
场景需求
- 目标:将
DateTime
字段从默认的 ISO 8601 格式("2025-07-19T17:11:39Z"
)转为"yyyy-MM-dd"
格式。 - 代码实现:
using System; using System.Text.Json; using System.Text.Json.Serialization; // 自定义 DateTime 转换器 public class CustomDateTimeConverter : JsonConverter<DateTime> { private const string DateFormat = "yyyy-MM-dd"; // 反序列化:JSON -> DateTime public override DateTime Read( ref Utf8JsonReader reader, Twww.devze.comype typeToConvert, JsonSerializerOptions options) { // 处理 null 值 if (reader.TokenType == JsonTokenType.Null) return default; // 读取 JSON 字符串 var dateString = reader.GetString(); if (DateTime.TryParseExact(dateString, DateFormat, null, System.Globalization.DateTimeStyles.None, out var result)) { return result; } throw new JsonException($"Invalid date format. Expected '{DateFormat}' but got '{dateString}'"); } // 序列化:DateTime -> JSON public override void Write( Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options) { // 格式化为指定格式 writer.WriteStringValue(value.ToString(DateFormat)); } }
使用示例
public class Person { public string Name { get; set; } [JsonConverter(typeof(CustomDateTimeConverter))] // 绑定转换器 public DateTime Birthday { get; set; } } // 测试代码 var person = new Person { Name = "Alice", Birthday = new DateTime(2025, 7, 19) }; var options = new JsonSerializerOptions { WriteIndented = true, Converters = { new CustomDateTimeConverter() } // 注册全局转换器 }; string json = JsonSerializer.Serialize(person, options); Console.WriteLine(json); // 输出: {"Name":"Alice","Birthday":"2025-07-19"}
技术细节
Utf8JsonReader
:高性能的 JSON 解析器,直接操作 UTF-8 缓冲区,避免字符串转换开销。Utf8JsonWriter
:直接生成 UTF-8 字节流,减少中间字符串分配。- 异常处理:
JsonException
是序列化框架的标准异常类型。
三、工厂模式:处理开放式泛型类型
3.1 适用场景
- 开放式泛型类型(如
List<T>
、Dictionary<TKey, TValue>
)。 - 多态类型(如
Enum
或动态类型)。
3.2 核心方法
CanConvert
:判断是否支持当前类型。CreateConverter
:动态创建特定类型的转换器实例。
3.3 实战案例:泛型集合转换器
场景需求
- 目标:支持任意
List<T>
类型的序列化,其中T
可能是int
、string
或自定义类型。
代码实现
using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; // 工厂类:处理 List<T> public class ListConverterFactory : JsonConverterFactory { // 判断是否支持当前类型 public override bool CanConvert(Type typeToConvert) { // 仅支持 List<T> 类型 return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(List<>); } // 动态创建转换器 public override JsonConverter CreateConverter( Type typeToConvert, JsonSerializerOptions options) { // 获取 T 的类型 var genericType = typeToConvert.GetGenericArguments()[0]; // 创建泛型转换器实例 return (JsonConverter)Activator.CreateInstance( typeof(ListConverter<>).MakeGenericType(genericType)); } } // 泛型转换器:处理 List<T> public class ListConverter<T> : JsonConverter<List<T>> { // 反序列化:JSON -> List<T> public override List<T> Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType != JsonTokenType.StartArray) throw new JsonException("Expected start of array"); var list = new List<T>(); while (reader.Read()) { if (reader.TokenType == JsonTokenType.EndArray) break; var item = JsonSerializer.Deserialize<T>(ref reader, options); list.Add(item); } return list; } // 序列化:List<T> -> JSON public override void Write( Utf8JsonWriter writer, List<T> value, JsonSerializerOptions options) { writer.WriteStartArray(); foreach (var item in value) { JsonSerializer.Serialize(writer, item, options); } writer.WriteEndArray(); } }
使用示例
public class Product { public string Name { get; set; } public List&androidlt;int> Ratings { get; set; } 编程} // 注册工厂转换器 var options = new JsonSerializerOptions { WriteIndented = true, Converters = { new ListConverterFactory() } }; var product = new Product { Name = "Laptop", Ratings = new List<int> { 5, 4, 5 } }; string json = JsonSerializer.Serialize编程(product, options); Console.WriteLine(json); // 输出: {"Name":"Laptop","Ratings":[5,4,5]}
技术细节
- 泛型反射:通过
MakeGenericType
动态创建泛型转换器。 - 递归序列化:
JsonSerializer.Deserialize<T>()
自动处理嵌套类型。
四、性能优化技巧
4.1 避免频繁创建对象
- 复用
JsonSerializerOptions
:全局配置并复用,避免重复解析设置。 - 缓存
JsonConverter
实例:在工厂模式中缓存已创建的转换器。
4.2 使用高性能 API
Utf8JsonReader
/Utf8JsonWriter
:比JsonTextReader
快 30%+,且内存占用更低。- Span 支持:避免不必要的字符串分配。
4.3 内存优化
- 异步处理:对于大型数据,使用
JsonSerializer.DeserializeAsync
。 - 流式处理:结合
Stream
逐块读写,避免一次性加载大文件。
五、高级场景:科学计数法转字符串
5.1 问题描述
数据库中的 decimal(18,2)
字段在 JSON 中可能被序列化为科学计数法:
{ "Price": "1.23E+08" }
而前端期望的是:
{ "Price": 123000000 }
5.2 解决方案
public class DecimalConverter : JsonConverter<decimal> { public override decimal Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 解析 JSON 字符串为 decimal return decimal.Parse(reader.GetString()!); } public override void Write( Utf8JsonWriter writer, decimal value, JsonSerializerOptions options) { // 将 decimal 转为固定格式的字符串 writer.WriteStringValue(value.ToString("F2")); } }
绑定模型
public class Product { [JsonConverter(typeof(DecimalConverter))] public decimal Price { get; set; } }
六、常见陷阱与解决方案
6.1 CanConvert 方法误判
- 问题:未正确覆盖
CanConvert
,导致转换器无法匹配目标类型。 - 解决方案:在工厂模式中,严格判断类型是否符合预期。
6.2 嵌套类型处理失败
- 问题:转换器未处理嵌套对象(如
Dictionary<string, List<int>>
)。 - 解决方案:递归调用
JsonSerializer
,或手动拆解结构。
6.3 线程安全问题
- 问题:共享的
JsonSerializerOptions
被多个线程修改。 - 解决方案:使用
ThreadLocal<JsonSerializerOptions>
或每次创建新实例。
七、自定义转换器的设计哲学
模式 | 适用类型 | 核心方法 | 典型场景 |
---|---|---|---|
基本模式 | 非泛型/封闭式泛型 | Read / Write | 日期格式化、枚举映射 |
工厂模式 | 开放式泛型/多态 | CanConvert / CreateConverter | 泛型集合、动态类型 |
设计原则:
- 最小化依赖:避免在转换器中引入复杂逻辑。
- 高内聚低耦合:每个转换器只处理一种类型或模式。
- 性能优先:优先使用
Utf8JsonReader
和Utf8JsonWriter
。 - 可测试性:通过单元测试验证转换器的正确性。
代码模板
基本模式模板
public class CustomConverter<T> : JsonConverter<T> { public override T Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 实现反序列化逻辑 } public override void Write( Utf8JsonWriter writer, T value, JsonSerializerOptions options) { // 实现序列化逻辑 } }
工厂模式模板
public class CustomConverterFactory : JsonConverterFactory { public override bool CanConvert(Type typeToConvert) { // 判断是否支持类型 } public override JsonConverter CreateConverter( Type typeToConvert, JsonSerializerOptions options) { // 动态创建转换器 } }
以上就是.NET中自定义JSON转换器的实战指南的详细内容,更多关于.NET自定义JSON转换器的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论