通过自定义属性(json.net)从序列化中排除属性

我需要能够控制一个类的某些属性是如何被序列化的。 最简单的情况是[ScriptIgnore] 。 但是,我只希望这些属性能够被我所处理的这个特定的序列化情况所尊重 – 如果应用程序中的其他下游模块也想要序列化这些对象,则这些属性都不应该妨碍。

所以我的想法是在属性上使用自定义属性MyAttribute ,并使用一个知道查找该属性的钩子初始化JsonSerializer的特定实例。

乍一看,我没有看到JSON.NET中的任何可用钩子点将为当前属性提供PropertyInfo来执行此类检查 – 只有属性的值。 我错过了什么吗? 或者更好的方法来解决这个问题?

你有几个select。 我build议你在阅读下面的内容之前阅读关于这个主题的Json.Net文档。

文章提出了两种方法:

  1. 创build一个方法,返回一个基于Json.Net将遵循的命名约定的bool值,以确定是否序列化属性。
  2. 创build一个忽略该属性的自定义合约parsing器。

在这两者中,我赞成后者。 完全跳过属性 – 仅使用它们忽略所有forms的序列化属性。 相反,创build一个自定义合约parsing器来忽略有问题的属性,并且只有当你想忽略该属性时才使用合约parsing器,而让这个类的其他用户自由地序列化这个属性或者不按照他们自己的想法。

编辑为了避免链接腐烂,我从文章张贴有问题的代码

 public class ShouldSerializeContractResolver : DefaultContractResolver { public new static readonly ShouldSerializeContractResolver Instance = new ShouldSerializeContractResolver(); protected override JsonProperty CreateProperty( MemberInfo member, MemberSerialization memberSerialization ) { JsonProperty property = base.CreateProperty( member, memberSerialization ); if( property.DeclaringType == typeof(Employee) && property.PropertyName == "Manager" ) { property.ShouldSerialize = instance => { // replace this logic with your own, probably just // return false; Employee e = (Employee)instance; return e.Manager != e; }; } return property; } } 

这里是一个通用的可重用的“忽略属性”parsing器基于接受的答案 :

 /// <summary> /// Special JsonConvert resolver that allows you to ignore properties. See https://stackoverflow.com/a/13588192/1037948 /// </summary> public class IgnorableSerializerContractResolver : DefaultContractResolver { protected readonly Dictionary<Type, HashSet<string>> Ignores; public IgnorableSerializerContractResolver() { this.Ignores = new Dictionary<Type, HashSet<string>>(); } /// <summary> /// Explicitly ignore the given property(s) for the given type /// </summary> /// <param name="type"></param> /// <param name="propertyName">one or more properties to ignore. Leave empty to ignore the type entirely.</param> public void Ignore(Type type, params string[] propertyName) { // start bucket if DNE if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet<string>(); foreach (var prop in propertyName) { this.Ignores[type].Add(prop); } } /// <summary> /// Is the given property for the given type ignored? /// </summary> /// <param name="type"></param> /// <param name="propertyName"></param> /// <returns></returns> public bool IsIgnored(Type type, string propertyName) { if (!this.Ignores.ContainsKey(type)) return false; // if no properties provided, ignore the type entirely if (this.Ignores[type].Count == 0) return true; return this.Ignores[type].Contains(propertyName); } /// <summary> /// The decision logic goes here /// </summary> /// <param name="member"></param> /// <param name="memberSerialization"></param> /// <returns></returns> protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); if (this.IsIgnored(property.DeclaringType, property.PropertyName) // need to check basetype as well for EF -- @per comment by user576838 || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName)) { property.ShouldSerialize = instance => { return false; }; } return property; } } 

和用法:

 var jsonResolver = new IgnorableSerializerContractResolver(); // ignore single property jsonResolver.Ignore(typeof(Company), "WebSites"); // ignore single datatype jsonResolver.Ignore(typeof(System.Data.Objects.DataClasses.EntityObject)); var jsonSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore, ContractResolver = jsonResolver }; 

使用JsonIgnore属性。

例如,要排除Id

 public class Person { [JsonIgnore] public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } 

这是一个基于drzaus优秀的串行器合约的方法,它使用lambdaexpression式。 只需将其添加到相同的类。 毕竟谁不喜欢编译器来检查它们呢?

 public IgnorableSerializerContractResolver Ignore<TModel>(Expression<Func<TModel, object>> selector) { MemberExpression body = selector.Body as MemberExpression; if (body == null) { UnaryExpression ubody = (UnaryExpression)selector.Body; body = ubody.Operand as MemberExpression; if (body == null) { throw new ArgumentException("Could not get property name", "selector"); } } string propertyName = body.Member.Name; this.Ignore(typeof (TModel), propertyName); return this; } 

您现在可以轻松而stream利地忽略属性:

 contract.Ignore<Node>(node => node.NextNode) .Ignore<Node>(node => node.AvailableNodes); 

我不在乎将属性名称设置为string,以防万一他们改变它会破坏我的其他代码。

我对需要序列化的对象有几种“视图模式”,所以我最终在合约parsing器(构造函数参数提供的视图模式)中做了这样的事情:

 protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); if (viewMode == ViewModeEnum.UnregisteredCustomer && member.GetCustomAttributes(typeof(UnregisteredCustomerAttribute), true).Length == 0) { property.ShouldSerialize = instance => { return false; }; } return property; } 

我的物品看起来像这样的地方:

 public interface IStatement { [UnregisteredCustomer] string PolicyNumber { get; set; } string PlanCode { get; set; } PlanStatus PlanStatus { get; set; } [UnregisteredCustomer] decimal TotalAmount { get; } [UnregisteredCustomer] ICollection<IBalance> Balances { get; } void SetBalances(IBalance[] balances); } 

这样做的缺点就是parsing器中有点反思,但我认为值得拥有更多可维护的代码。