You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Newtonsoft序列化泛型类且不封装内部类型

问题:Newtonsoft.Json序列化时展开Generic的Data属性,避免嵌套

现有泛型类和实体类定义如下:

public class Generic<T>
    where T : class
{
    public bool IsSuccess { get; set; }
    public string Message { get; set; }
    public T Data { get; set; }
}

public class Specific
{
    public string Name { get; set; }
    public int Id { get; set; }
}

声明对象示例:

var wrappedSpecific = new Generic<Specific>
{
    IsSuccess = true,
    Message = "Success",
    Data = new Specific
    {
        Name = "Test",
        Id = 1,
    }
};

默认序列化会得到嵌套格式:

{"IsSuccess":true,"Message":"Success","Data":{"Name":"Test","Id":1}}

需求:希望序列化时将Data属性内的字段直接平级输出,得到如下格式,且不采用Specific : Generic的继承方式,保证Specific类可独立复用:

{"IsSuccess":true,"Message":"Success","Name":"Test","Id":1}

解决方案:自定义JsonConverter

通过实现JsonConverter来控制序列化逻辑,将Generic<T>的基础属性和Data内的属性合并输出:

通用转换器代码

public class GenericConverter : JsonConverter
{
    // 判断当前类型是否为Generic<T>
    public override bool CanConvert(Type objectType)
    {
        return objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(Generic<>);
    }

    // 自定义序列化逻辑
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var genericType = value.GetType();
        // 获取Generic<T>自身的属性(排除Data)
        var baseProperties = genericType.GetProperties()
            .Where(p => p.Name != nameof(Generic<object>.Data));

        // 获取Data属性的值
        var dataProp = genericType.GetProperty(nameof(Generic<object>.Data));
        var dataValue = dataProp.GetValue(value);

        writer.WriteStartObject();

        // 序列化Generic<T>的基础属性
        foreach (var prop in baseProperties)
        {
            writer.WritePropertyName(prop.Name);
            serializer.Serialize(writer, prop.GetValue(value));
        }

        // 序列化Data对象的所有属性(如果Data不为null)
        if (dataValue != null)
        {
            var dataType = dataValue.GetType();
            foreach (var dataProperty in dataType.GetProperties())
            {
                writer.WritePropertyName(dataProperty.Name);
                serializer.Serialize(writer, dataProperty.GetValue(dataValue));
            }
        }

        writer.WriteEndObject();
    }

    // 反序列化逻辑可根据需求实现(以下为示例框架)
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var genericInstance = Activator.CreateInstance(objectType);
        var dataType = objectType.GetGenericArguments()[0];
        var dataInstance = Activator.CreateInstance(dataType);

        var jsonObject = JObject.Load(reader);

        // 给Generic<T>的基础属性赋值
        foreach (var prop in objectType.GetProperties().Where(p => p.Name != nameof(Generic<object>.Data)))
        {
            var value = jsonObject[prop.Name]?.ToObject(prop.PropertyType, serializer);
            prop.SetValue(genericInstance, value);
        }

        // 给Data对象的属性赋值
        foreach (var prop in dataType.GetProperties())
        {
            var value = jsonObject[prop.Name]?.ToObject(prop.PropertyType, serializer);
            prop.SetValue(dataInstance, value);
        }

        // 将Data对象赋值给Generic<T>的Data属性
        objectType.GetProperty(nameof(Generic<object>.Data)).SetValue(genericInstance, dataInstance);

        return genericInstance;
    }
}

使用方式

序列化时传入自定义转换器:

var settings = new JsonSerializerSettings();
settings.Converters.Add(new GenericConverter());

var json = JsonConvert.SerializeObject(wrappedSpecific, settings);
// 输出结果:{"IsSuccess":true,"Message":"Success","Name":"Test","Id":1}

说明

  • 该转换器支持所有Generic<T>类型,无需为每个实体类单独编写逻辑
  • 若不需要反序列化,可省略ReadJson方法的实现(或抛出NotImplementedException)
  • 保持了Specific类的独立性,可在不需要Generic<T>包装的场景中直接复用

内容的提问来源于stack exchange,提问作者MPelletier

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 13:10:19