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

.NET 4.6.2中序列化不可修改子类构成的对象图并忽略指定成员的最佳策略

.NET 4.6.2中序列化不可修改子类构成的对象图并忽略指定成员的最佳策略

针对你遇到的这种无法直接修改基类/派生类代码,但需要忽略特定成员序列化的场景,最稳妥且高效的方案是使用Newtonsoft.Json的自定义ContractResolver(假设你用的是Newtonsoft.Json,这是.NET 4.6.2生态中最常用的JSON序列化库)。先帮你梳理下之前尝试踩坑的原因,再给出可行的解决思路:

为什么之前的尝试出了问题?

  1. MetadataType属性不生效:Newtonsoft.Json默认不会识别MetadataType标注的元数据类,你需要额外编写复杂逻辑让序列化器读取这些元数据,反而得不偿失。
  2. JsonConverter的递归陷阱:你遇到的栈溢出,本质是因为在WriteJson中调用默认序列化时,当前的Converter又被重复应用,导致无限递归。如果要走Converter路线,必须手动规避递归,但这会增加实现复杂度。

推荐方案:自定义ContractResolver

ContractResolver是Newtonsoft.Json中控制序列化规则的核心组件,它可以全局定义哪些成员需要序列化/忽略,不需要修改原类代码,也不会有递归风险。

具体实现

  1. 创建自定义ContractResolver,继承自DefaultContractResolver,重写CreateProperties方法过滤需要忽略的成员:
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

public class IgnoreSpecificBaseMembersResolver : DefaultContractResolver
{
    // 存储需要忽略的基类及其对应的成员名
    private readonly Dictionary<Type, HashSet<string>> _ignoredMemberMap;

    public IgnoreSpecificBaseMembersResolver()
    {
        _ignoredMemberMap = new Dictionary<Type, HashSet<string>>
        {
            { typeof(SomeBaseClass), new HashSet<string> { nameof(SomeBaseClass.DontSerialize) } },
            { typeof(SomeOtherBaseClass), new HashSet<string> { nameof(SomeOtherBaseClass.DontSerialize) } },
            { typeof(YetAnotherBaseClass), new HashSet<string> { nameof(YetAnotherBaseClass.DontSerialize) } }
        };
    }

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        // 先获取默认的所有可序列化属性
        var properties = base.CreateProperties(type, memberSerialization);

        // 检查当前类型是否是目标基类的子类(或基类本身)
        foreach (var (baseType, ignoredMembers) in _ignoredMemberMap)
        {
            if (baseType.IsAssignableFrom(type))
            {
                // 过滤掉需要忽略的成员
                properties = properties.Where(p => !ignoredMembers.Contains(p.PropertyName)).ToList();
                break; // 找到匹配的基类后停止,避免重复过滤
            }
        }

        return properties;
    }
}
  1. 使用这个Resolver执行序列化:
// 建议缓存Resolver实例,因为每次创建会有性能开销
private static readonly IgnoreSpecificBaseMembersResolver _ignoreResolver = new IgnoreSpecificBaseMembersResolver();

// 构建要序列化的对象图(示例数据)
var objectGraph = new ObjectGraph
{
    SomeDerivedCollection = new List<SomeDerivedClass>
    {
        new SomeDerivedClass
        {
            DontSerialize = 123,
            SomeOtherDerivedCollection = new List<SomeOtherDerivedClass>
            {
                new SomeOtherDerivedClass { DontSerialize = 456 }
            },
            YetAnotherDerivedClass = new YetAnotherDerivedClass { DontSerialize = 789 }
        }
    }
};

// 配置序列化设置
var serializeSettings = new JsonSerializerSettings
{
    ContractResolver = _ignoreResolver,
    Formatting = Formatting.Indented // 可选,格式化输出JSON
};

// 执行序列化
var jsonResult = JsonConvert.SerializeObject(objectGraph, serializeSettings);

方案优势

  • 零侵入:完全不需要修改原类代码,完美适配你无法修改基类/派生类的场景。
  • 无递归风险:基于默认序列化逻辑扩展,只是过滤掉不需要的属性,不会触发无限递归。
  • 灵活可扩展:如果后续需要忽略其他类的成员,只需在_ignoredMemberMap中添加新的类型和成员名即可。

备选方案:修复JsonConverter的递归问题

如果你坚持要使用JsonConverter,可以通过在WriteJson中创建不带当前Converter的临时序列化器来避免递归:

using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class IgnoreDontSerializeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        // 匹配所有需要处理的基类及其子类
        return typeof(SomeBaseClass).IsAssignableFrom(objectType)
            || typeof(SomeOtherBaseClass).IsAssignableFrom(objectType)
            || typeof(YetAnotherBaseClass).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        // 创建临时序列化器,移除当前Converter以避免递归
        var tempSerializer = new JsonSerializer();
        foreach (var converter in serializer.Converters.Where(c => c.GetType() != GetType()))
        {
            tempSerializer.Converters.Add(converter);
        }

        // 把对象转为JObject,移除不需要的属性
        var jObj = JObject.FromObject(value, tempSerializer);
        jObj.Remove(nameof(SomeBaseClass.DontSerialize)); // 三个基类的该属性名相同,直接移除
        jObj.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        // 如果需要反序列化,可参考WriteJson逻辑实现,这里示例只处理序列化
        throw new NotImplementedException("反序列化逻辑需根据需求实现");
    }
}

使用时只需将Converter加入序列化设置:

var settings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new IgnoreDontSerializeConverter() },
    Formatting = Formatting.Indented
};
var json = JsonConvert.SerializeObject(objectGraph, settings);

不过这个方案的性能略低于ContractResolver,因为每次都要额外转换为JObject,适合小范围的序列化场景。

备注:内容来源于stack exchange,提问作者Brian Sweeney

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.14 11:25:29