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

自定义Json.Net序列化对象的$type属性值

实现方案:自定义类型属性替代TypeNameHandling

刚好做过类似的需求,给你一套完整的实现方案,完全适配你现有的CamelCasePropertyNamesContractResolver契约解析器,同时关闭自动生成的$type:

核心思路

既然只需要在客户端展示类型信息、不需要反序列化时依赖它,我们可以通过自定义契约解析器动态添加类型属性,同时关闭TypeNameHandling避免自动生成$type。这样既满足类型标识需求,又完全掌控属性的名称和取值逻辑。

步骤1:修改现有契约解析器

在你继承自CamelCasePropertyNamesContractResolver的自定义解析器中,重写CreateProperties方法,添加我们自定义的类型属性,并通过IValueProvider控制属性的取值:

public class CustomCamelCaseContractResolver : CamelCasePropertyNamesContractResolver
{
    // 可选:如果需要自定义类型别名,这里可以维护类型与别名的映射
    private static readonly Dictionary<Type, string> TypeAliasMap = new()
    {
        { typeof(YourDerivedType1), "user-profile" },
        { typeof(YourDerivedType2), "order-detail" }
        // 按需添加更多类型映射
    };

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = base.CreateProperties(type, memberSerialization);

        // 仅给需要标识类型的对象添加属性(比如所有引用类型/特定基类的子类)
        // 这里可以根据你的需求调整判断逻辑
        if (typeof(object).IsAssignableFrom(type) && !type.IsValueType && type != typeof(string))
        {
            var customTypeProperty = new JsonProperty
            {
                PropertyName = "customType", // 自定义属性名,自动遵循驼峰规则
                Readable = true,
                Writable = false, // 不允许反序列化写入,符合你的需求
                ValueProvider = new CustomTypeValueProvider(type),
                DefaultValueHandling = DefaultValueHandling.Ignore,
                Order = -1 // 让类型属性排在JSON最前面,可选
            };

            properties.Insert(0, customTypeProperty);
        }

        return properties;
    }

    // 自定义ValueProvider,负责提供类型标识的值
    private class CustomTypeValueProvider : IValueProvider
    {
        private readonly Type _targetType;

        public CustomTypeValueProvider(Type targetType)
        {
            _targetType = targetType;
        }

        public object GetValue(object target)
        {
            // 优先使用自定义别名,没有则返回类型全名/名称
            if (TypeAliasMap.TryGetValue(_targetType, out var alias))
            {
                return alias;
            }
            // 这里可以根据需求修改,比如返回_type.Name、_type.FullName等
            return _targetType.Name;
        }

        public void SetValue(object target, object value)
        {
            // 因为Writable=false,这个方法不会被调用,留空即可
        }
    }
}

步骤2:配置Json.Net序列化器

关闭TypeNameHandling,并使用我们的自定义契约解析器:

var serializerSettings = new JsonSerializerSettings
{
    ContractResolver = new CustomCamelCaseContractResolver(),
    TypeNameHandling = TypeNameHandling.None, // 彻底关闭自动生成$type
    Formatting = Formatting.Indented, // 可选,美化JSON输出
    // 保留你原本的其他配置(比如NullValueHandling等)
};

// 序列化示例
var sampleObj = new YourDerivedType1 { Id = 123, Name = "Test User" };
var json = JsonConvert.SerializeObject(sampleObj, serializerSettings);

步骤3:验证输出

序列化后的JSON会包含我们自定义的customType属性,且没有自动生成的$type:

{
  "customType": "user-profile",
  "id": 123,
  "name": "Test User"
}

可选优化

  • 精准控制类型范围:如果只需要给特定基类的子类添加类型属性,把CreateProperties里的判断逻辑改成typeof(YourBaseClass).IsAssignableFrom(type) && type != typeof(YourBaseClass)即可。
  • 动态类型标识:如果需要从数据库/配置文件读取类型别名,可以把TypeAliasMap改成从外部加载的逻辑,无需硬编码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:10:29