在C#项目中使用System.Text.Json处理JSON数据时,遇到字段格式特殊、类型映射复杂的情况,默认的序列化反序列化逻辑无法适配,这时候就需要通过自定义Converter来实现特殊处理逻辑。

System.Text.Json自定义Converter基础
System.Text.Json提供了JsonConverter基类,开发者可以通过继承该类并重写对应的方法,实现自定义的反序列化逻辑。核心需要重写的方法是Read,负责从JSON流中读取数据并转换为目标类型。
自定义Converter的基本结构如下:
using System.Text.Json;
using System.Text.Json.Serialization;
public class CustomJsonConverter<T> : JsonConverter<T>
{
// 重写Read方法,实现反序列化逻辑
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// 自定义处理逻辑
throw new NotImplementedException();
}
// 重写Write方法,实现序列化逻辑(如果需要序列化也需要自定义)
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
// 自定义处理逻辑
throw new NotImplementedException();
}
}
场景一:处理特殊格式的日期字段
假设接口返回的JSON中日期字段是yyyyMMdd格式,而C#中对应的属性是DateTime类型,默认的反序列化会失败,这时候可以自定义日期Converter。
首先定义目标实体类:
public class OrderInfo
{
public string OrderId { get; set; }
// 日期字段是特殊格式,需要自定义Converter处理
public DateTime CreateTime { get; set; }
}
然后实现日期格式的自定义Converter:
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
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)
{
// 读取JSON中的字符串值
string dateStr = reader.GetString();
// 按照指定格式解析日期
if (DateTime.TryParseExact(dateStr, _format, null, System.Globalization.DateTimeStyles.None, out DateTime result))
{
return result;
}
throw new JsonException($"无法将字符串{dateStr}转换为DateTime格式");
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
// 序列化时按照指定格式输出
writer.WriteStringValue(value.ToString(_format));
}
}
使用的时候在实体属性上标注Converter即可:
public class OrderInfo
{
public string OrderId { get; set; }
[JsonConverter(typeof(CustomDateTimeConverter), "yyyyMMdd")]
public DateTime CreateTime { get; set; }
}
测试反序列化代码:
using System;
using System.Text.Json;
class Program
{
static void Main()
{
string json = "{"OrderId":"1001","CreateTime":"20240520"}";
OrderInfo order = JsonSerializer.Deserialize<OrderInfo>(json);
Console.WriteLine($"订单ID:{order.OrderId},创建时间:{order.CreateTime:yyyy-MM-dd}");
}
}
场景二:处理枚举的自定义映射
如果JSON中枚举字段是字符串,而C#中枚举是数字,或者JSON中的字符串和枚举成员名称不一致,也需要自定义Converter。
比如定义枚举:
public enum OrderStatus
{
Pending = 0,
Paid = 1,
Shipped = 2,
Completed = 3
}
JSON中返回的订单状态是待支付、已支付这样的中文,和枚举成员不匹配,自定义Converter实现映射:
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
public class OrderStatusConverter : JsonConverter<OrderStatus>
{
private static readonly Dictionary<string, OrderStatus> _statusMap = new Dictionary<string, OrderStatus>
{
{"待支付", OrderStatus.Pending},
{"已支付", OrderStatus.Paid},
{"已发货", OrderStatus.Shipped},
{"已完成", OrderStatus.Completed}
};
public override OrderStatus Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
string statusStr = reader.GetString();
if (_statusMap.TryGetValue(statusStr, out OrderStatus status))
{
return status;
}
throw new JsonException($"无法识别的订单状态:{statusStr}");
}
public override void Write(Utf8JsonWriter writer, OrderStatus value, JsonSerializerOptions options)
{
// 找到对应的中文描述输出
foreach (var item in _statusMap)
{
if (item.Value == value)
{
writer.WriteStringValue(item.Key);
return;
}
}
writer.WriteNumberValue((int)value);
}
}
实体类标注Converter:
public class OrderInfo
{
public string OrderId { get; set; }
[JsonConverter(typeof(CustomDateTimeConverter), "yyyyMMdd")]
public DateTime CreateTime { get; set; }
[JsonConverter(typeof(OrderStatusConverter))]
public OrderStatus Status { get; set; }
}
场景三:处理嵌套复杂对象
如果JSON结构嵌套复杂,比如某个字段是动态结构,或者需要合并多个字段转换为一个对象,也可以通过自定义Converter实现。
假设JSON结构如下,需要把user_info下的name和age合并到UserInfo对象中:
{
"order_id": "1001",
"user_info": {
"name": "张三",
"age": 25
},
"total_price": 199.9
}
定义实体类:
public class OrderDetail
{
public string OrderId { get; set; }
public UserInfo UserInfo { get; set; }
public decimal TotalPrice { get; set; }
}
public class UserInfo
{
public string Name { get; set; }
public int Age { get; set; }
}
实现自定义Converter处理嵌套结构:
using System.Text.Json;
using System.Text.Json.Serialization;
public class OrderDetailConverter : JsonConverter<OrderDetail>
{
public override OrderDetail Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// 使用JsonDocument解析整个JSON对象
using JsonDocument doc = JsonDocument.ParseValue(ref reader);
JsonElement root = doc.RootElement;
OrderDetail order = new OrderDetail();
// 读取普通字段
order.OrderId = root.GetProperty("order_id").GetString();
order.TotalPrice = root.GetProperty("total_price").GetDecimal();
// 处理嵌套的user_info字段
JsonElement userInfoElement = root.GetProperty("user_info");
order.UserInfo = new UserInfo
{
Name = userInfoElement.GetProperty("name").GetString(),
Age = userInfoElement.GetProperty("age").GetInt32()
};
return order;
}
public override void Write(Utf8JsonWriter writer, OrderDetail value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("order_id", value.OrderId);
writer.WriteStartObject("user_info");
writer.WriteString("name", value.UserInfo.Name);
writer.WriteNumber("age", value.UserInfo.Age);
writer.WriteEndObject();
writer.WriteNumber("total_price", value.TotalPrice);
writer.WriteEndObject();
}
}
在实体类上标注Converter即可正常使用:
[JsonConverter(typeof(OrderDetailConverter))]
public class OrderDetail
{
public string OrderId { get; set; }
public UserInfo UserInfo { get; set; }
public decimal TotalPrice { get; set; }
}
全局注册Converter
如果不想在每一个实体属性上单独标注Converter,也可以在JsonSerializerOptions中全局注册,所有符合类型的属性都会使用该Converter:
using System.Text.Json;
class Program
{
static void Main()
{
JsonSerializerOptions options = new JsonSerializerOptions();
// 全局注册日期Converter
options.Converters.Add(new CustomDateTimeConverter("yyyyMMdd"));
// 全局注册枚举Converter
options.Converters.Add(new OrderStatusConverter());
string json = "{"OrderId":"1001","CreateTime":"20240520","Status":"已支付"}";
OrderInfo order = JsonSerializer.Deserialize<OrderInfo>(json, options);
Console.WriteLine($"订单状态:{order.Status}");
}
}
注意事项
- 自定义Converter的
Read方法需要正确处理Utf8JsonReader的读取位置,避免读取错位导致解析失败。 - 如果Converter需要处理可空类型,建议继承
JsonConverter<T?>或者在逻辑中处理null值的情况。 - 全局注册的Converter会覆盖默认的逻辑,如果有特殊属性需要不同的处理,可以在属性上单独标注Converter,属性标注的优先级高于全局注册。
C#System.Text.Json自定义ConverterJSON反序列化修改时间:2026-07-04 22:54:43