.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)其它相关文章!
加载中,请稍侯......
精彩评论