Escludi proprietà dalla serializzazione tramite attributo personalizzato (json.net)

Devo essere in grado di controllare come / se determinate proprietà su una class sono serializzate. Il caso più semplice è [ScriptIgnore] . Tuttavia, voglio solo che questi attributi siano onorati per questa specifica situazione di serializzazione su cui sto lavorando – se altri moduli a valle nell’applicazione vogliono anche serializzare questi oggetti, nessuno di questi attributi dovrebbe interferire.

Quindi il mio pensiero è di usare un attributo personalizzato MyAttribute sulle proprietà e inizializzare l’istanza specifica di JsonSerializer con un hook che sappia cercare quell’attributo.

A prima vista, non vedo nessuno dei punti di aghook disponibili in JSON.NET fornirà a PropertyInfo la proprietà corrente per effettuare tale ispezione, solo il valore della proprietà. Mi sto perdendo qualcosa? O un modo migliore per avvicinarsi a questo?

Hai alcune opzioni. Vi consiglio di leggere l’ articolo della documentazione di Json.Net sull’argomento prima di leggere di seguito.

L’articolo presenta due metodi:

  1. Creare un metodo che restituisca un valore bool base a una convenzione di denominazione seguita da Json.Net per determinare se serializzare la proprietà o meno.
  2. Creare un resolver di contratto personalizzato che ignori la proprietà.

Dei due, preferisco quest’ultimo. Salta gli attributi del tutto – li usa solo per ignorare le proprietà attraverso tutte le forms di serializzazione. Invece, crea un risolutore di contratto personalizzato che ignori la proprietà in questione e utilizzi il risolutore di contratto solo quando desideri ignorare la proprietà, lasciando liberi gli altri utenti della class di serializzare la proprietà o non a loro piacimento.

Modifica Per evitare il link put, sto postando il codice in questione dall’articolo

 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; } } 

Ecco un risolutore generico riutilizzabile “ignora proprietà” basato sulla risposta accettata :

 ///  /// Special JsonConvert resolver that allows you to ignore properties. See https://stackoverflow.com/a/13588192/1037948 ///  public class IgnorableSerializerContractResolver : DefaultContractResolver { protected readonly Dictionary> Ignores; public IgnorableSerializerContractResolver() { this.Ignores = new Dictionary>(); } ///  /// Explicitly ignore the given property(s) for the given type ///  ///  /// one or more properties to ignore. Leave empty to ignore the type entirely. public void Ignore(Type type, params string[] propertyName) { // start bucket if DNE if (!this.Ignores.ContainsKey(type)) this.Ignores[type] = new HashSet(); foreach (var prop in propertyName) { this.Ignores[type].Add(prop); } } ///  /// Is the given property for the given type ignored? ///  ///  ///  ///  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); } ///  /// The decision logic goes here ///  ///  ///  ///  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; } } 

E l’uso:

 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 }; 

Usa l’attributo JsonIgnore .

Ad esempio, per escludere Id :

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

Ecco un metodo basato sull’eccellente contratto di serializzazione di drzaus che usa espressioni lambda. Basta aggiungerlo alla stessa class. Dopo tutto, chi non preferisce che il compilatore faccia il controllo per loro?

 public IgnorableSerializerContractResolver Ignore(Expression> 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; } 

Ora puoi ignorare le proprietà in modo semplice e scorrevole:

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

Non mi interessa impostare i nomi delle proprietà come stringhe, nel caso in cui esse cambino, si rompono il mio altro codice.

Ho avuto diverse “modalità di visualizzazione” sugli oggetti che avevo bisogno di serializzare, quindi ho finito per fare qualcosa del genere nel risolutore di contratto (modalità di visualizzazione fornita dall’argomento costruttore):

 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; } 

Dove i miei oggetti assomigliano a questo:

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

Il lato negativo di questo sarebbe il riflesso del risolutore, ma penso che valga la pena avere un codice più gestibile.

Ho avuto buoni risultati con la combinazione di entrambe le risposte drzaus e Steve Rukuts. Tuttavia, devo affrontare un problema quando imposto JsonPropertyAttribute con un nome o un limite diverso per la proprietà. Per esempio:

 [JsonProperty("username")] public string Username { get; set; } 

Include UnderlyingName in considerazione risolve il problema:

 protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) { JsonProperty property = base.CreateProperty(member, memberSerialization); if (this.IsIgnored(property.DeclaringType, property.PropertyName) || this.IsIgnored(property.DeclaringType, property.UnderlyingName) || this.IsIgnored(property.DeclaringType.BaseType, property.PropertyName) || this.IsIgnored(property.DeclaringType.BaseType, property.UnderlyingName)) { property.ShouldSerialize = instance => { return false; }; } return property; }