System.Text.Json是否有类似NewtonSoft ShouldSerialize()的嵌套序列化控制机制?
Unity 6中System.Text.Json实现条件序列化的替代方案
现状确认
System.Text.Json 6.0.0(Unity 6支持的最高版本)确实没有内置类似Newtonsoft.Json的ShouldSerializeXXX自动检测机制。默认情况下,要实现条件序列化,确实只能通过在TypeInfoResolver中全局配置修改逻辑,这种方式会让序列化条件和字段定义分离,增加维护成本。
更优实现方案
下面提供几种更贴近Newtonsoft使用习惯、维护更方便的方案,按推荐程度排序:
1. 自定义属性+全局TypeInfoResolver(推荐)
通过自定义属性标记需要条件序列化的字段,并关联对应的私有判断方法,再实现一个全局的TypeInfoResolver统一处理所有标记的字段,让条件逻辑和字段定义绑定在一起。
步骤1:定义条件序列化属性
using System; [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class ConditionalSerializeAttribute : Attribute { public string ConditionMethodName { get; } public ConditionalSerializeAttribute(string conditionMethodName) { ConditionMethodName = conditionMethodName; } }
步骤2:实现自定义TypeInfoResolver
using System; using System.Linq; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization.Metadata; public class ConditionalSerializeTypeInfoResolver : DefaultJsonTypeInfoResolver { public override JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions options) { var typeInfo = base.GetTypeInfo(type, options); foreach (var property in typeInfo.Properties) { // 检查字段是否标记了自定义属性 var attr = property.AttributeProvider .GetCustomAttributes(typeof(ConditionalSerializeAttribute), false) .FirstOrDefault() as ConditionalSerializeAttribute; if (attr == null) continue; // 获取关联的私有判断方法 var conditionMethod = type.GetMethod( attr.ConditionMethodName, BindingFlags.Instance | BindingFlags.NonPublic); if (conditionMethod == null || conditionMethod.ReturnType != typeof(bool)) continue; // 替换属性的ShouldSerialize逻辑 property.ShouldSerialize = (obj, value) => (bool)conditionMethod.Invoke(obj, null); } return typeInfo; } }
步骤3:在业务类中使用
public class SubClass { [ConditionalSerialize(nameof(ShouldSerializeMyProperty))] public int MyProperty { get; set; } public string OtherProperty { get; set; } // 私有判断方法,逻辑和字段放在一起 private bool ShouldSerializeMyProperty() { // 这里写你的条件判断逻辑,比如值大于0才序列化 return MyProperty > 0; } } // 包含嵌套容器的顶级类 public class TopLevelClass { public List<SubClass> SubItems { get; set; } public Dictionary<string, SubClass> SubMap { get; set; } }
步骤4:配置序列化选项
var serializeOptions = new JsonSerializerOptions { TypeInfoResolver = new ConditionalSerializeTypeInfoResolver(), // 可选:根据需要开启格式化 WriteIndented = true }; // 序列化示例 var topLevelObj = new TopLevelClass { SubItems = new List<SubClass> { new SubClass { MyProperty = 5, OtherProperty = "Valid" }, new SubClass { MyProperty = 0, OtherProperty = "Invalid" } } }; var json = JsonSerializer.Serialize(topLevelObj, serializeOptions); // 输出结果中,第二个SubClass的MyProperty会被忽略
这种方式只需要一次全局配置,所有字段的条件逻辑都写在字段附近,和Newtonsoft的用法几乎一致,维护成本极低。
2. 针对特定类的自定义JsonConverter
如果部分类的条件序列化逻辑非常特殊,可以单独为其编写JsonConverter,直接控制序列化过程:
using System.Text.Json; using System.Text.Json.Serialization; public class SubClassConverter : JsonConverter<SubClass> { public override SubClass Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { // 实现反序列化逻辑,按需编写 return JsonSerializer.Deserialize<SubClass>(ref reader, options); } public override void Write(Utf8JsonWriter writer, SubClass value, JsonSerializerOptions options) { writer.WriteStartObject(); // 条件判断是否写入字段 if (value.MyProperty > 0) { writer.WriteNumber(nameof(SubClass.MyProperty), value.MyProperty); } // 其他字段正常序列化 writer.WriteString(nameof(SubClass.OtherProperty), value.OtherProperty); writer.WriteEndObject(); } }
使用时在序列化选项中添加转换器:
var options = new JsonSerializerOptions(); options.Converters.Add(new SubClassConverter()); var json = JsonSerializer.Serialize(topLevelObj, options);
这种方式适合逻辑复杂的单个类,但需要每个类单独编写转换器,通用性不如第一种方案。
3. 使用DTO(数据传输对象)
如果项目规模不大,可以创建专门的DTO类,只包含需要序列化的字段,在序列化前将原对象映射到DTO:
public class SubClassDTO { public int? MyProperty { get; set; } public string OtherProperty { get; set; } public static SubClassDTO FromOriginal(SubClass original) { var dto = new SubClassDTO { OtherProperty = original.OtherProperty }; // 条件判断是否包含字段 if (original.MyProperty > 0) { dto.MyProperty = original.MyProperty; } return dto; } } // 顶级类的DTO public class TopLevelClassDTO { public List<SubClassDTO> SubItems { get; set; } public Dictionary<string, SubClassDTO> SubMap { get; set; } public static TopLevelClassDTO FromOriginal(TopLevelClass original) { return new TopLevelClassDTO { SubItems = original.SubItems.Select(SubClassDTO.FromOriginal).ToList(), SubMap = original.SubMap.ToDictionary(kv => kv.Key, kv => SubClassDTO.FromOriginal(kv.Value)) }; } }
序列化时直接使用DTO:
var dto = TopLevelClassDTO.FromOriginal(topLevelObj); var json = JsonSerializer.Serialize(dto);
这种方式逻辑直观,不需要处理System.Text.Json的复杂配置,但需要额外编写映射代码,适合字段较少的场景。
内容的提问来源于stack exchange,提问作者Jeffery Chow
相关产品推荐
相关产品推荐

