Eigenschap uitsluiten van serialisatie via aangepast attribuut (JSON.net)

Ik moet in staat zijn om te bepalen hoe / of bepaalde eigenschappen op een klasse zijn geserialiseerd. De eenvoudigste zaak is [ScriptIgnore]. Ik wil echter alleen dat deze kenmerken worden gehonoreerd voor deze specifieke serialisatie-situatie waar ik aan werk – als andere modules stroomafwaarts in de applicatie ook willen serialiseren, zou geen van deze kenmerken in de weg moeten staan.

Dus mijn gedachte is om een ​​aangepast attribuut MyAttributeop de eigenschappen te gebruiken en het specifieke exemplaar van Jsonserializer te initialiseren met een haak die het kenmerk weet.

In het eerste gezicht zie ik geen van de beschikbare haakpunten in JSON.net, de PropertyInfovoor de huidige eigenschap om zo’n inspectie te doen – alleen de waarde van de accommodatie. Mis ik iets? Of een betere manier om dit te benaderen?


Antwoord 1, Autoriteit 100%

Hier is een generieke herbruikbare “Negeer onroerend goed” resolver op basis van Het geaccepteerde antwoord :

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

en gebruik:

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

Antwoord 2, Autoriteit 96%

Gebruik de JsonIgnoreattribuut

Bijvoorbeeld om Iduit te sluiten:

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

Antwoord 3, autoriteit 68%

Je hebt een paar opties. Ik raad u aan het artikel over de Json.Net-documentatie te lezen over het onderwerpvoordat u hieronder leest.

Het artikel presenteert twee methoden:

  1. Maak een methode die een bool-waarde retourneert op basis van een naamgevingsconventie die Json.Net zal volgen om te bepalen of de eigenschap al dan niet moet worden geserialiseerd.
  2. Maak een aangepaste contractoplosser die de eigenschap negeert.

Van de twee geef ik de voorkeur aan het laatste. Sla attributen helemaal over — gebruik ze alleen om eigenschappen in alle vormen van serialisatie te negeren. Maak in plaats daarvan een aangepaste contractoplosser die de eigenschap in kwestie negeert, en gebruik de contractoplosser alleen wanneer u de eigenschap wilt negeren, zodat andere gebruikers van de klasse vrij zijn om de eigenschap al dan niet naar eigen inzicht te serialiseren.

BewerkenOm linkrot te voorkomen, plaats ik de betreffende code uit het artikel

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

Antwoord 4, autoriteit 41%

Hier is een methode gebaseerd op het uitstekende serializer-contract van drzaus dat lambda-expressies gebruikt. Voeg het gewoon toe aan dezelfde klasse. Wie geeft er tenslotte niet de voorkeur aan dat de compiler de controle voor hen doet?

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

U kunt eigenschappen nu gemakkelijk en vloeiend negeren:

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

Antwoord 5, autoriteit 4%

Het maakt me niet uit om de eigenschapsnamen als strings in te stellen, in het geval dat ze ooit zouden veranderen, zou mijn andere code breken.

Ik had verschillende “weergavemodi” voor de objecten die ik moest serialiseren, dus uiteindelijk deed ik zoiets als dit in de contractoplosser (weergavemodus geleverd door constructorargument):

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

waar mijn objecten er als volgt uitzien:

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

Het nadeel hiervan zou het bit van reflectie in de resolver zijn, maar ik denk dat het de moeite waard is om een ​​meer onderhoudbare code te hebben.


Antwoord 6

Ik had goede resultaten met de combinatie van zowel DRZAUS- als Steve Rukuts-antwoorden. Ik ga echter voor een probleem wanneer ik JSONPROPERTYATTRIBUTE met een andere naam of caps voor de accommodatie instel. Bijvoorbeeld:

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

Neem onderliggende naam in overweging op het probleem:

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

Antwoord 7

Als u bereid bent om F # (of gewoon een API te gebruiken, niet geoptimaliseerd voor c #), de fsharp .Jsonskippable Bibliotheek Hiermee kunt u op een eenvoudige en sterk getypte manier beheersen om een ​​bepaald eigenschap op te nemen bij het serialiseren (en bepaalt of een eigenschap is opgenomen bij deserializing), en bovendien om uitsluiting afzonderlijk van de bijzonderheid te besturen / te bepalen. (Volledige openbaarmaking: ik ben de auteur van de bibliotheek.)

Other episodes