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

如何为同一对象类型实现两种JSON序列化格式?

同一对象实现多种JSON序列化方式的可选方案

问题背景

我有一个需要序列化的List<DTOModule>,类定义如下:

public class DTOModule
{
    public string? Name { get; set; }
    public string? Prefix { get; set; }
    public string? Description { get; set; }
    public string? ImagePath { get; set; }
    [JsonIgnore]
    public DateTime? ExpiryDate { get; set; }
    [JsonIgnore]
    public bool? Visible { get; set; }
    [JsonIgnore]
    public bool? Enabled { get; set; }
    [JsonIgnore]
    public virtual bool? IsEnabled
    {
        get { return (Visible == false || Enabled == false || ExpiryDate < DateTime.Now) ? false : null; }
    }
    [JsonIgnore]
    public string? DisabledDescription { get; set; }
}

默认情况下,用以下代码序列化时仅包含Name、Prefix、Description和ImagePath属性:

private void GetModules(List<DTOModule> moduleClaims)
{
    var moduleJson = Newtonsoft.Json.JsonConvert.SerializeObject(modules);
    // 后续逻辑
}

现在需要在代码另一部分覆盖默认规则,让序列化输出Prefix、ExpiryDate、IsEnabled和DisabledDescription属性。已知可以通过LINQ转匿名对象实现,但想要更可靠的方案,请问有哪些可选方式?

可选方案

1. 自定义Json.NET契约解析器(ContractResolver)

通过实现IContractResolver,根据不同场景动态控制属性的序列化状态,这是Json.NET最灵活的扩展方式之一。

先定义两个解析器分别对应默认和自定义规则:

// 默认规则解析器:保留类中[JsonIgnore]的标记,只序列化未被忽略的属性
public class DefaultModuleResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var props = base.CreateProperties(type, memberSerialization);
        return props.Where(p => !p.Ignored).ToList();
    }
}

// 自定义规则解析器:强制包含指定属性,忽略其他属性
public class CustomModuleResolver : DefaultContractResolver
{
    private readonly HashSet<string> _allowedProps = new HashSet<string>
    {
        "Prefix", "ExpiryDate", "IsEnabled", "DisabledDescription"
    };

    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var props = base.CreateProperties(type, memberSerialization);
        foreach (var prop in props)
        {
            // 取消目标属性的忽略标记,其他属性强制忽略
            prop.Ignored = !_allowedProps.Contains(prop.PropertyName);
        }
        return props;
    }
}

使用时指定对应的解析器即可:

// 默认序列化
var defaultSettings = new JsonSerializerSettings
{
    ContractResolver = new DefaultModuleResolver()
};
var defaultJson = JsonConvert.SerializeObject(modules, defaultSettings);

// 自定义规则序列化
var customSettings = new JsonSerializerSettings
{
    ContractResolver = new CustomModuleResolver()
};
var customJson = JsonConvert.SerializeObject(modules, customSettings);

2. 为不同场景创建专用DTO类(最直观可靠)

为每个序列化场景定义独立的DTO类,通过映射工具(如AutoMapper)在原类和专用DTO之间转换,这种方式逻辑清晰,避免后期维护时的规则混淆。

比如创建用于状态输出的专用DTO:

public class ModuleStatusDTO
{
    public string? Prefix { get; set; }
    public DateTime? ExpiryDate { get; set; }
    public bool? IsEnabled { get; set; }
    public string? DisabledDescription { get; set; }
}

配置AutoMapper映射并序列化:

// 提前配置映射规则
var mapperConfig = new MapperConfiguration(cfg =>
{
    cfg.CreateMap<DTOModule, ModuleStatusDTO>();
});
var mapper = mapperConfig.CreateMapper();

// 转换并序列化
var statusDtos = mapper.Map<List<ModuleStatusDTO>>(modules);
var customJson = JsonConvert.SerializeObject(statusDtos);

3. 使用Json.NET的条件序列化(Condition属性)

如果使用Json.NET 12及以上版本,可以利用JsonProperty的Condition属性,结合动态上下文控制属性是否序列化。

先修改原类的属性标记:

public class DTOModule
{
    [JsonProperty(Condition = JsonPropertyCondition.WhenWritingDefault)]
    public string? Name { get; set; }
    
    public string? Prefix { get; set; }
    
    [JsonProperty(Condition = JsonPropertyCondition.WhenWritingDefault)]
    public string? Description { get; set; }
    
    [JsonProperty(Condition = JsonPropertyCondition.WhenWritingDefault)]
    public string? ImagePath { get; set; }
    
    [JsonProperty(Condition = JsonPropertyCondition.Never)]
    public DateTime? ExpiryDate { get; set; }
    
    [JsonProperty(Condition = JsonPropertyCondition.Never)]
    public bool? Visible { get; set; }
    
    [JsonProperty(Condition = JsonPropertyCondition.Never)]
    public bool? Enabled { get; set; }
    
    [JsonProperty(Condition = JsonPropertyCondition.Never)]
    public virtual bool? IsEnabled
    {
        get { return (Visible == false || Enabled == false || ExpiryDate < DateTime.Now) ? false : null; }
    }
    
    [JsonProperty(Condition = JsonPropertyCondition.Never)]
    public string? DisabledDescription { get; set; }
}

再定义动态修改条件的解析器:

public class DynamicConditionResolver : DefaultContractResolver
{
    private readonly bool _useCustomRules;

    public DynamicConditionResolver(bool useCustomRules)
    {
        _useCustomRules = useCustomRules;
    }

    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var prop = base.CreateProperty(member, memberSerialization);
        if (_useCustomRules)
        {
            var allowedProps = new[] { "Prefix", "ExpiryDate", "IsEnabled", "DisabledDescription" };
            prop.Condition = allowedProps.Contains(prop.PropertyName) 
                ? JsonPropertyCondition.Always 
                : JsonPropertyCondition.Never;
        }
        return prop;
    }
}

使用示例:

// 默认序列化
var defaultSettings = new JsonSerializerSettings
{
    ContractResolver = new DynamicConditionResolver(false)
};
var defaultJson = JsonConvert.SerializeObject(modules, defaultSettings);

// 自定义规则序列化
var customSettings = new JsonSerializerSettings
{
    ContractResolver = new DynamicConditionResolver(true)
};
var customJson = JsonConvert.SerializeObject(modules, customSettings);

4. 使用DataContract多规则配置

将原类改为DataContract标记,通过自定义DataContractResolver筛选需要序列化的属性,适合WCF或使用DataContract序列化的场景。

修改类定义:

[DataContract]
public class DTOModule
{
    [DataMember]
    public string? Name { get; set; }
    
    [DataMember]
    public string? Prefix { get; set; }
    
    [DataMember]
    public string? Description { get; set; }
    
    [DataMember]
    public string? ImagePath { get; set; }
    
    [DataMember]
    public DateTime? ExpiryDate { get; set; }
    
    [DataMember]
    public bool? Visible { get; set; }
    
    [DataMember]
    public bool? Enabled { get; set; }
    
    [DataMember]
    public virtual bool? IsEnabled
    {
        get { return (Visible == false || Enabled == false || ExpiryDate < DateTime.Now) ? false : null; }
    }
    
    [DataMember]
    public string? DisabledDescription { get; set; }
}

自定义解析器筛选属性:

public class CustomDataContractResolver : DataContractResolver
{
    private readonly HashSet<string> _allowedProps = new HashSet<string>
    {
        "Prefix", "ExpiryDate", "IsEnabled", "DisabledDescription"
    };

    public override IEnumerable<KeyValuePair<string, object>> GetSerializableMembers(Type type, object? instance)
    {
        var members = base.GetSerializableMembers(type, instance);
        return members.Where(kv => _allowedProps.Contains(kv.Key));
    }

    // 其他接口方法直接调用默认实现
    public override bool TryResolveType(Type type, Type declaredType, DataContractResolver knownTypeResolver, out DataContract dataContract)
    {
        return knownTypeResolver.TryResolveType(type, declaredType, null, out dataContract);
    }

    public override XmlDictionaryString ResolveName(Type type, Type declaredType, DataContractResolver knownTypeResolver)
    {
        return knownTypeResolver.ResolveName(type, declaredType, null);
    }

    public override object? ResolveType(XmlDictionaryString typeName, XmlDictionaryString typeNamespace, DataContractResolver knownTypeResolver)
    {
        return knownTypeResolver.ResolveType(typeName, typeNamespace, null);
    }
}

使用示例:

// 默认序列化(包含所有DataMember属性)
var defaultSerializer = new DataContractJsonSerializer(typeof(List<DTOModule>));
using var defaultMs = new MemoryStream();
defaultSerializer.WriteObject(defaultMs, modules);
var defaultJson = Encoding.UTF8.GetString(defaultMs.ToArray());

// 自定义规则序列化
var customSerializer = new DataContractJsonSerializer(
    typeof(List<DTOModule>), null, int.MaxValue, false, new CustomDataContractResolver(), false);
using var customMs = new MemoryStream();
customSerializer.WriteObject(customMs, modules);
var customJson = Encoding.UTF8.GetString(customMs.ToArray());

内容的提问来源于stack exchange,提问作者Hank

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 23:35:19