Wyklucz właściwość z serializacji za pomocą niestandardowego atrybutu (json.net)

Muszę być w stanie kontrolować, jak/czy pewne właściwości na klasie są serializowane. Najprostszym przypadkiem jest [ScriptIgnore]. Jednak chcę tylko, aby te atrybuty były honorowane dla tej konkretnej sytuacji serializacji, nad którą pracuję - jeśli inne moduły w aplikacji również chcą serializować te obiekty, żaden z tych atrybutów nie powinien wchodzić w drogę.

Więc moim zdaniem jest użycie niestandardowego atrybutu MyAttribute na właściwościach i zainicjalizowanie konkretnej instancji JsonSerializer z Hookiem, który wie, aby szukać tego atrybutu.

Na pierwszy rzut oka nie widzę żadnego z dostępnych punktów zaczepu w JSON.NET dostarczy PropertyInfo dla bieżącej właściwości, aby wykonać taką inspekcję - tylko wartość właściwości. Coś przeoczyłem? Albo lepszy sposób na to?

Author: Rex M, 2012-11-27

6 answers

Masz kilka opcji. Polecam przeczytać Json.Net dokumentacja artykuł na ten temat przed przeczytaniem poniżej.

Artykuł przedstawia dwie metody:

  1. Utwórz metodę, która zwraca wartość {[1] } w oparciu o konwencję nazewnictwa, która Json.Net nastąpi, aby określić, czy chcesz serializować właściwość.
  2. Utwórz niestandardowy mechanizm rozwiązywania umów, który ignoruje właściwość.
[[2]] z tych dwóch, popieram to drugie. Pomiń atrybuty w sumie -- używaj ich tylko do ignorowania właściwości we wszystkich formach serializacji. Zamiast tego Utwórz niestandardowy program do rozwiązywania umów, który ignoruje daną właściwość, i używaj go tylko wtedy, gdy chcesz zignorować tę właściwość, pozostawiając innym użytkownikom klasy swobodę serializacji Właściwości lub nie według ich własnego kaprysu.

Edit aby uniknąć gnicia linków, zamieszczam kod z Artykułu

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;
   }
}
 39
Author: Randolpho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-20 16:41:49

Oto ogólne rozwiązanie "ignore property" wielokrotnego użytku oparte na zaakceptowanej odpowiedzi :

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

I użycie:

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 };
 63
Author: drzaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 10:31:31

Użyj atrybutu JsonIgnore.

Na przykład, aby wykluczyć Id:

public class Person {
    [JsonIgnore]
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
 51
Author: ramonesteban78,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-11 16:14:15

Oto metoda oparta na doskonałej umowie serializera drzausa, która wykorzystuje wyrażenia lambda. Po prostu dodaj go do tej samej klasy. W końcu, kto nie woli, aby kompilator je sprawdzał?

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

Możesz teraz łatwo i płynnie ignorować właściwości:

contract.Ignore<Node>(node => node.NextNode)
    .Ignore<Node>(node => node.AvailableNodes);
 24
Author: Steve Rukuts,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-20 18:39:48

Nie obchodzi mnie ustawianie nazw właściwości jako ciągów znaków, na wypadek gdyby kiedykolwiek zmienili to złamałoby mój drugi kod.

Miałem kilka "trybów widoku" na obiektach, które musiałem serializować, więc skończyłem robiąc coś takiego w rozwiązywaniu umowy (tryb widoku dostarczany przez argument konstruktora):

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

Gdzie moje obiekty wyglądają tak:

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

Minusem tego byłoby trochę refleksji w resolverze, ale myślę, że warto mieć więcej konserwacji kod.

 3
Author: frattaro,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-07 19:36:53

Miałem dobre wyniki z połączeniem obu odpowiedzi drzausa i Steve ' a Rukutsa. Jednak napotykam problem, gdy ustawiam jsonpropertyattribute z inną nazwą lub caps dla właściwości. Na przykład:

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

Uwzględnienie nazwy podkładowej rozwiązuje problem:

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;
}
 1
Author: Baron Ch'ng,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 18:01:08