自定义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
相关产品推荐
相关产品推荐

