《C#自定义将各种对象转换成JSON格式的类》
在.NET开发中,JSON(JavaScript Object Notation)已成为数据交换的主流格式。无论是Web API、前后端通信还是配置文件,JSON都因其轻量级、易读性和跨语言支持而备受青睐。然而,.NET自带的System.Text.Json
或Newtonsoft.Json
虽然功能强大,但在处理复杂对象(如循环引用、自定义序列化逻辑、多态类型等)时,往往需要额外的配置或扩展。本文将深入探讨如何自定义一个通用的JSON转换类,支持多种对象类型的序列化与反序列化,同时解决常见痛点问题。
一、JSON序列化的核心原理
JSON序列化是将对象转换为JSON字符串的过程,反序列化则是逆向操作。.NET中主流的序列化库有两种:
- System.Text.Json(.NET Core 3.0+内置):高性能、低内存分配,但功能相对基础。
- Newtonsoft.Json(第三方库):功能丰富,支持动态类型、自定义转换器等高级特性。
无论是哪种库,核心逻辑均围绕以下步骤展开:
- 遍历对象的属性或字段。
- 根据类型映射规则将值转换为JSON兼容格式(如字符串、数字、布尔值、数组、对象)。
- 处理特殊场景(如循环引用、继承、接口类型)。
二、自定义JSON转换类的设计目标
自定义类的核心目标是提供灵活、可扩展的序列化/反序列化能力,具体需求包括:
- 支持基本类型、集合、自定义类、匿名对象等。
- 处理循环引用(避免栈溢出)。
- 自定义属性名映射(如C#属性名与JSON键名不一致)。
- 忽略特定属性(如敏感字段)。
- 支持多态类型(如通过接口或基类序列化派生类)。
三、实现自定义JSON转换类
以下基于System.Text.Json
实现一个简化版的自定义转换类,后续可扩展为支持Newtonsoft.Json
的双引擎版本。
1. 基础接口设计
定义一个通用接口IJsonConverter
,允许注册自定义转换逻辑:
public interface IJsonConverter
{
bool CanConvert(Type typeToConvert);
void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options);
object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options);
}
2. 核心转换类实现
创建CustomJsonSerializer
类,封装序列化/反序列化逻辑:
public class CustomJsonSerializer
{
private readonly List _converters = new();
private readonly JsonSerializerOptions _options;
public CustomJsonSerializer()
{
_options = new JsonSerializerOptions
{
WriteIndented = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
}
public void RegisterConverter(IJsonConverter converter)
{
_converters.Add(converter);
}
public string Serialize(object value)
{
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
if (value == null)
{
writer.WriteNullValue();
}
else
{
var converter = _converters.FirstOrDefault(c => c.CanConvert(value.GetType()));
if (converter != null)
{
converter.Write(writer, value, _options);
}
else
{
JsonSerializer.Serialize(writer, value, _options);
}
}
writer.Flush();
return Encoding.UTF8.GetString(stream.ToArray());
}
public T Deserialize(string json)
{
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(json));
using var reader = new Utf8JsonReader(stream);
reader.Read(); // 移动到第一个token
var type = typeof(T);
var converter = _converters.FirstOrDefault(c => c.CanConvert(type));
if (converter != null)
{
return (T)converter.Read(ref reader, type, _options);
}
else
{
return JsonSerializer.Deserialize(json, _options);
}
}
}
3. 处理循环引用
循环引用是序列化时的常见问题。通过维护一个已序列化对象的哈希表,可避免无限递归:
public class CycleReferenceConverter : IJsonConverter
{
private readonly Dictionary
4. 自定义属性名映射
通过特性(Attribute)标记属性与JSON键的映射关系:
[AttributeUsage(AttributeTargets.Property)]
public class JsonPropertyNameAttribute : Attribute
{
public string Name { get; }
public JsonPropertyNameAttribute(string name)
{
Name = name;
}
}
public class PropertyNameConverter : IJsonConverter
{
public bool CanConvert(Type typeToConvert) => true;
public void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
writer.WriteStartObject();
var props = value.GetType().GetProperties();
foreach (var prop in props)
{
var attr = prop.GetCustomAttribute();
var propName = attr?.Name ?? prop.Name;
writer.WritePropertyName(propName);
JsonSerializer.Serialize(writer, prop.GetValue(value), options);
}
writer.WriteEndObject();
}
public object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// 反序列化逻辑需匹配属性名,此处简化处理
throw new NotImplementedException();
}
}
5. 多态类型支持
通过$type
字段标记实际类型,反序列化时动态创建实例:
public class PolymorphicConverter : IJsonConverter
{
public bool CanConvert(Type typeToConvert) => true;
public void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
{
writer.WriteStartObject();
writer.WriteString("$type", value.GetType().AssemblyQualifiedName);
// 序列化实际属性
var props = value.GetType().GetProperties();
foreach (var prop in props)
{
writer.WritePropertyName(prop.Name);
JsonSerializer.Serialize(writer, prop.GetValue(value), options);
}
writer.WriteEndObject();
}
public object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
// 读取$type并创建实例
while (reader.Read() && reader.TokenType != JsonTokenType.PropertyName) { }
if (reader.GetString() != "$type") throw new JsonException("Missing $type");
reader.Read();
var typeName = reader.GetString();
var type = Type.GetType(typeName);
if (type == null) throw new JsonException($"Type {typeName} not found");
// 读取剩余属性并反序列化
var json = reader.GetString(); // 简化处理,实际需重新解析
return JsonSerializer.Deserialize(json, type, options);
}
}
四、完整示例与测试
组合上述转换器,测试复杂对象序列化:
// 定义测试类
public class Person
{
[JsonPropertyName("full_name")]
public string Name { get; set; }
public int Age { get; set; }
public Person Spouse { get; set; } // 循环引用
}
// 测试代码
var serializer = new CustomJsonSerializer();
serializer.RegisterConverter(new CycleReferenceConverter());
serializer.RegisterConverter(new PropertyNameConverter());
var person1 = new Person { Name = "Alice", Age = 30 };
var person2 = new Person { Name = "Bob", Age = 32 };
person1.Spouse = person2;
person2.Spouse = person1;
var json = serializer.Serialize(person1);
Console.WriteLine(json);
输出结果(简化版):
{
"$id": "0",
"full_name": "Alice",
"Age": 30,
"Spouse": {
"$id": "1",
"full_name": "Bob",
"Age": 32,
"Spouse": { "$ref": "0" }
}
}
五、性能优化与扩展建议
-
缓存反射结果:通过
System.Reflection.Emit
或表达式树生成快速序列化委托。 -
异步支持:使用
Utf8JsonStreamWriter
处理大文件或流式数据。 -
多引擎适配:通过工厂模式支持
System.Text.Json
和Newtonsoft.Json
切换。 - AOT兼容性:针对iOS/Android的AOT编译优化反射调用。
六、总结
自定义JSON转换类的核心在于通过扩展点(如转换器接口)实现灵活控制。本文实现的类虽为基础版本,但已覆盖循环引用、属性映射、多态等关键场景。实际开发中,可根据需求进一步优化性能或集成更复杂的逻辑(如动态类型、版本兼容等)。
关键词:C#、JSON序列化、自定义转换器、循环引用、多态类型、System.Text.Json、Newtonsoft.Json
简介:本文详细探讨如何在C#中自定义一个通用的JSON转换类,支持复杂对象序列化与反序列化,解决循环引用、属性映射、多态类型等痛点问题,并提供基础实现与优化建议。