总目录
前言
在 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}
// 方式1
var options = new JsonSerializerOptions
{
Converters = { new JsonStringEnumConverter() }
};
json = JsonSerializer.Serialize(order, options);
Console.WriteLine(json);
// 输出:{"Id":1,"Name":"Order0001","CountryType":"China","Status":"Pending"}
// 方式2
var 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 知识汇总
希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。
参考资料: