使用System.Text.Json根据另一属性值动态忽略序列化对象属性
实现方案
我们可以通过「自定义特性 + System.Text.Json 序列化配置」实现需求,以下为完整实现步骤:
1. 定义条件包含特性
首先创建用于标记属性的自定义特性,存储判断序列化所需的参数:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] public class IncludeIfListIncludesAttribute : Attribute { /// <summary> /// 用于判断的列表属性名称 /// </summary> public string ListPropertyName { get; } /// <summary> /// 序列化当前属性要求列表必须包含的值 /// </summary> public string RequiredValue { get; } public IncludeIfListIncludesAttribute(string listPropertyName, string requiredValue) { ListPropertyName = listPropertyName; RequiredValue = requiredValue; } }
注:这里调整了示例中的参数形式,改为传入属性名称字符串,搭配nameof语法使用既安全又便于反射读取属性值。
2. 实现动态属性过滤逻辑
如果你使用的是 System.Text.Json 7.0及以上版本,可以通过JsonTypeInfo修改器实现全局通用的属性过滤逻辑,不需要为每个业务类单独写转换器:
public static void ConditionalSerializeFilter(JsonTypeInfo typeInfo) { // 仅处理类类型的序列化 if (typeInfo.Kind != JsonTypeInfoKind.Object) return; foreach (var jsonProperty in typeInfo.Properties) { // 读取属性上的自定义特性 var includeAttr = jsonProperty.AttributeProvider? .GetCustomAttributes(typeof(IncludeIfListIncludesAttribute), false) .Cast<IncludeIfListIncludesAttribute>() .FirstOrDefault(); if (includeAttr == null) continue; // 绑定序列化判断逻辑 jsonProperty.ShouldSerialize = (instance, propertyValue) => { // 反射获取列表属性值 var listProp = instance.GetType().GetProperty(includeAttr.ListPropertyName); if (listProp?.GetValue(instance) is not IEnumerable<string> listValue) return false; // 列表为空默认不序列化当前属性 // 判断是否满足序列化条件 return listValue.Contains(includeAttr.RequiredValue); }; } }
3. 配置序列化参数并测试
修改你的业务类添加特性标记,然后配置序列化参数即可使用:
// 业务类添加特性标记 public class MyClass { public List<string> Types { get; set; } [IncludeIfListIncludes(nameof(Types), "Type1")] public string PropertyValidForType1 {get; set;} [IncludeIfListIncludes(nameof(Types), "Type2")] public string PropertyValidForType2 {get; set;} [IncludeIfListIncludes(nameof(Types), "Type3")] public string PropertyValidForType3 {get; set;} } // 序列化调用 var serializeOptions = new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { ConditionalSerializeFilter } }, WriteIndented = true // 可选:格式化输出方便查看 }; var c = new MyClass { Types = new List<string> { "Type1", "Type3" }, PropertyValidForType1 = "A", PropertyValidForType2 = "B", PropertyValidForType3 = "C", }; var jsonString = JsonSerializer.Serialize(c, serializeOptions); Console.WriteLine(jsonString);
输出结果
{ "Types": [ "Type1", "Type3" ], "PropertyValidForType1": "A", "PropertyValidForType3": "C" }
低版本兼容方案(System.Text.Json <7.0)
如果使用的是低于7.0的版本,可以为业务类实现自定义转换器:
public class MyClassJsonConverter : JsonConverter<MyClass> { public override MyClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 按需实现反序列化逻辑 throw new NotImplementedException(); } public override void Write(Utf8JsonWriter writer, MyClass value, JsonSerializerOptions options) { writer.WriteStartObject(); // 写入固定的Types属性 writer.WritePropertyName(nameof(MyClass.Types)); JsonSerializer.Serialize(writer, value.Types, options); // 遍历其他属性判断是否需要序列化 var properties = typeof(MyClass).GetProperties().Where(p => p.Name != nameof(MyClass.Types)); foreach (var prop in properties) { var attr = prop.GetCustomAttribute<IncludeIfListIncludesAttribute>(); if (attr == null || (value.Types != null && value.Types.Contains(attr.RequiredValue))) { writer.WritePropertyName(prop.Name); JsonSerializer.Serialize(writer, prop.GetValue(value), prop.PropertyType, options); } } writer.WriteEndObject(); } }
使用时把转换器加入序列化配置即可:
var options = new JsonSerializerOptions { Converters = { new MyClassJsonConverter() }, WriteIndented = true };
内容的提问来源于stack exchange,提问作者Kevin Smith
相关产品推荐
相关产品推荐

