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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.02 05:54:53