C#如何通过自定义特性指定Json序列化的属性子集
System.Text.Json按自定义特性筛选属性序列化实现方案
你可以通过自定义System.Text.Json的类型元数据解析逻辑实现需求,不需要修改现有业务类结构,支持灵活传入多个特性类型指定要序列化的属性分组,性能和原生序列化保持一致。
前置:自定义特性定义
你现有的分组特性只需要确保标记为可作用于属性即可,示例定义如下,可直接复用你已有的代码:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class CustomNameAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class CustomAddressAttribute : Attribute { }
你给出的MyClass类不需要做任何调整,直接使用即可。
核心实现:序列化扩展方法
通过给JsonSerializerOptions配置自定义的类型信息修改器,在序列化前过滤掉未标记目标特性的属性,封装为通用扩展方法后即可实现你期望的调用效果:
using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization.Metadata; public static class JsonAttributeFilterExtensions { /// <summary> /// 仅序列化标记了指定特性的属性 /// </summary> /// <param name="instance">待序列化对象实例</param> /// <param name="includeAttributeTypes">需要纳入序列化范围的属性标记特性类型集合</param> /// <param name="baseOptions">基础序列化配置,会复制配置避免修改全局/传入的原始配置</param> /// <returns>筛选属性后的JSON字符串</returns> public static string SerializeWithAttributeFilter<T>( this T instance, Type[] includeAttributeTypes, JsonSerializerOptions? baseOptions = null) { var options = baseOptions == null ? new JsonSerializerOptions() : new JsonSerializerOptions(baseOptions); options.TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { ApplyAttributeFilter(includeAttributeTypes.ToHashSet()) } }; return JsonSerializer.Serialize(instance, options); } private static Action<JsonTypeInfo> ApplyAttributeFilter(HashSet<Type> includeAttributes) { return typeInfo => { // 非对象类型(值类型、集合、字符串等)不做属性过滤 if (typeInfo.Kind != JsonTypeInfoKind.Object) return; // 倒序遍历移除不符合要求的属性,避免遍历过程中索引变化问题 for (int i = typeInfo.Properties.Count - 1; i >= 0; i--) { JsonPropertyInfo prop = typeInfo.Properties[i]; if (prop.AttributeProvider == null) { typeInfo.Properties.RemoveAt(i); continue; } // 检查当前属性是否标记了任意一个目标特性 bool hasMatch = prop.AttributeProvider .GetCustomAttributes(inherit: false) .Any(attr => includeAttributes.Contains(attr.GetType())); if (!hasMatch) { typeInfo.Properties.RemoveAt(i); } } }; } }
调用示例
var myClassInstance = new MyClass { UserFirstName = "王", UserLastName = "五", UserAddressLine1 = "解放路88号", UserAddressLine2 = "幸福小区3栋2单元" }; // 1. 仅序列化姓名分组属性(标记CustomNameAttribute的属性) string nameGroupJson = myClassInstance.SerializeWithAttributeFilter(new[] { typeof(CustomNameAttribute) }); // 输出:{"UserFirstName":"王","UserLastName":"五"} // 2. 仅序列化地址分组属性(标记CustomAddressAttribute的属性) string addressGroupJson = myClassInstance.SerializeWithAttributeFilter(new[] { typeof(CustomAddressAttribute) }); // 输出:{"UserAddressLine1":"解放路88号","UserAddressLine2":"幸福小区3栋2单元"} // 3. 同时序列化姓名+地址两个分组的属性 string allGroupJson = myClassInstance.SerializeWithAttributeFilter(new[] { typeof(CustomNameAttribute), typeof(CustomAddressAttribute) }); // 输出包含全部4个属性
补充说明
- 该实现兼容.NET 6及以上版本,不需要引入第三方序列化组件
- 如果你使用Newtonsoft.Json,核心逻辑一致,通过自定义
DefaultContractResolver重写属性筛选规则即可实现相同效果 - 支持属性标记多个分组特性的场景,只要属性标记的任意一个特性在传入的包含列表中,就会被序列化
- 如果需要实现排除指定特性属性的效果,只需要调整筛选逻辑,移除标记了传入特性的属性即可
内容的提问来源于stack exchange,提问作者Gabe
相关产品推荐
相关产品推荐

