Исключить свойство из сериализации через пользовательский атрибут (json.net)

Мне нужно иметь возможность контролировать, как / независимо от того, сериализованы ли определенные свойства в classе. Самый простой случай – [ScriptIgnore] . Тем не менее, я хочу, чтобы эти атрибуты были выполнены для этой конкретной ситуации сериализации, над которой я работаю, – если другие модули, расположенные ниже по течению в приложении, также хотят сериализовать эти объекты, ни один из этих атрибутов не должен мешать.

Поэтому я думал использовать пользовательский атрибут MyAttribute для свойств и инициализировать конкретный экземпляр JsonSerializer с помощью крючка, который знает, как искать этот атрибут.

На первый взгляд, я не вижу, чтобы какие-либо из доступных точек привязки в JSON.NET предоставили PropertyInfo для текущего свойства для такой проверки – только значение свойства. Я что-то упускаю? Или лучший способ приблизиться к этому?

У вас есть несколько вариантов. Я рекомендую вам прочитать статью по документации Json.Net по этому вопросу, прежде чем читать ниже.

В статье представлены два метода:

  1. Создайте метод, который возвращает значение bool на основе соглашения об именах, которое будет следовать Json.Net, чтобы определить, следует ли сериализовать свойство.
  2. Создайте собственный пользовательский разрешитель, который игнорирует свойство.

Из двух я одобряю последнее. Пропустить атрибуты вообще – используйте их только для игнорирования свойств во всех формах сериализации. Вместо этого создайте настраиваемый пользовательский разрешитель, который игнорирует соответствующее свойство, и используйте только разрешающую способность контракта, если вы хотите игнорировать свойство, оставляя других пользователей classа бесплатно сериализовать свойство или не по собственной прихоти.

Изменить Чтобы избежать гниения ссылок, я отправляю код, о котором идет речь, из статьи

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

Вот общий многоразовый «ignore property» resolver на основе принятого ответа :

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

И использование:

 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, который использует lambda-выражения. Просто добавьте его в тот же class. В конце концов, кто не хочет, чтобы компилятор делал для них проверку?

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

Теперь вы можете игнорировать свойства легко и плавно:

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

Я не хочу устанавливать имена свойств как строки, в случае, если они когда-либо меняются, это нарушит мой другой код.

У меня было несколько «режимов просмотра» для объектов, которые мне нужны для сериализации, поэтому я решил сделать что-то подобное в разрешении контракта (режим просмотра, предоставляемый аргументом конструктора):

 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 Balances { get; } void SetBalances(IBalance[] balances); } 

Недостатком этого было бы немного отражения в резольвере, но я думаю, что стоит иметь более удобный код.

У меня были хорошие результаты с сочетанием обоих слов drzaus и Steve Rukuts. Тем не менее, я сталкиваюсь с проблемой, когда я устанавливаю JsonPropertyAttribute с другим именем или шапками для свойства. Например:

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

Включите UnderlyingName, чтобы решить проблему:

 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; } 
  • C # JSON.NET - десериализация ответа, использующего необычную структуру данных
  • Deserialize JSON в динамический объект C #?
  • Джерси + jackson JSON формат даты формат - как изменить формат или использовать пользовательские JacksonJsonProvider
  • jQuery serializeArray не включает кнопку отправки, которая была нажата
  • Json.Net: свойство Serialize / Deserialize в качестве значения, а не как объект
  • Json.NET: десериализация вложенных словарей
  • Можете ли вы определить, был ли объект, десериализованный объект, отсутствием поля с classом JsonConvert в Json.NET
  • Сериализация OpenCV Mat_
  • Использование JSON.NET для возврата ActionResult
  • Задача не сериализуема: java.io.NotSerializableException при вызове функции закрытие только для classов не объектов
  • EF 4.1 - Code First - Ошибка серийной ссылки JSON
  • Interesting Posts
    Давайте будем гением компьютера.