位置: 文档库 > C#(.NET) > C#自定义将各种对象转换成JSON格式的类

C#自定义将各种对象转换成JSON格式的类

郁郁园中柳 上传于 2024-06-12 05:44

C#自定义将各种对象转换成JSON格式的类》

在.NET开发中,JSON(JavaScript Object Notation)已成为数据交换的主流格式。无论是Web API、前后端通信还是配置文件,JSON都因其轻量级、易读性和跨语言支持而备受青睐。然而,.NET自带的System.Text.JsonNewtonsoft.Json虽然功能强大,但在处理复杂对象(如循环引用、自定义序列化逻辑、多态类型等)时,往往需要额外的配置或扩展。本文将深入探讨如何自定义一个通用的JSON转换类,支持多种对象类型的序列化与反序列化,同时解决常见痛点问题。

一、JSON序列化的核心原理

JSON序列化是将对象转换为JSON字符串的过程,反序列化则是逆向操作。.NET中主流的序列化库有两种:

  • System.Text.Json(.NET Core 3.0+内置):高性能、低内存分配,但功能相对基础。
  • Newtonsoft.Json(第三方库):功能丰富,支持动态类型、自定义转换器等高级特性。

无论是哪种库,核心逻辑均围绕以下步骤展开:

  1. 遍历对象的属性或字段。
  2. 根据类型映射规则将值转换为JSON兼容格式(如字符串、数字、布尔值、数组、对象)。
  3. 处理特殊场景(如循环引用、继承、接口类型)。

二、自定义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 _references = new();
    private int _refId = 0;

    public bool CanConvert(Type typeToConvert) => true; // 通用转换器

    public void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
    {
        if (value == null)
        {
            writer.WriteNullValue();
            return;
        }

        var type = value.GetType();
        if (_references.TryGetValue(value, out var id))
        {
            writer.WriteStartObject();
            writer.WriteString("$ref", id.ToString());
            writer.WriteEndObject();
            return;
        }

        _references[value] = _refId++;
        writer.WriteStartObject();
        writer.WriteString("$id", (_refId - 1).ToString());

        // 递归序列化属性(需过滤循环引用)
        var props = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in props)
        {
            writer.WritePropertyName(prop.Name);
            var propValue = prop.GetValue(value);
            
            if (propValue != null && _references.ContainsKey(propValue))
            {
                writer.WriteStartObject();
                writer.WriteString("$ref", _references[propValue].ToString());
                writer.WriteEndObject();
            }
            else
            {
                JsonSerializer.Serialize(writer, propValue, options);
            }
        }

        writer.WriteEndObject();
    }

    public object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        // 反序列化逻辑需解析$id和$ref,此处简化处理
        throw new NotImplementedException();
    }
}

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" }
  }
}

五、性能优化与扩展建议

  1. 缓存反射结果:通过System.Reflection.Emit或表达式树生成快速序列化委托。
  2. 异步支持:使用Utf8JsonStreamWriter处理大文件或流式数据。
  3. 多引擎适配:通过工厂模式支持System.Text.JsonNewtonsoft.Json切换。
  4. AOT兼容性:针对iOS/Android的AOT编译优化反射调用。

六、总结

自定义JSON转换类的核心在于通过扩展点(如转换器接口)实现灵活控制。本文实现的类虽为基础版本,但已覆盖循环引用、属性映射、多态等关键场景。实际开发中,可根据需求进一步优化性能或集成更复杂的逻辑(如动态类型、版本兼容等)。

关键词:C#、JSON序列化、自定义转换器、循环引用、多态类型、System.Text.Json、Newtonsoft.Json

简介:本文详细探讨如何在C#中自定义一个通用的JSON转换类,支持复杂对象序列化与反序列化,解决循环引用、属性映射、多态类型等痛点问题,并提供基础实现与优化建议。

《C#自定义将各种对象转换成JSON格式的类.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档