如何为同一对象类型实现两种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
相关产品推荐
相关产品推荐

