2016-10-24 1 views
1

Compte tenu de la classe suivante:sérialisation Attribut personnalisé Valeurs

public class PayrollReport 
{ 
    [UiGridColumn(Name = "fullName",Visible = false,Width = "90")] 
    public string FullName { get; set; } 
    [UiGridColumn(Name = "weekStart", CellFilter = "date")] 
    public DateTime WeekStart { get; set; } 
} 

Et cette coutume attribut

[AttributeUsage(AttributeTargets.All)] 
public class UiGridColumn : Attribute 
{ 
    public string CellFilter { get; set; } 
    public string DisplayName { get; set; } 
    public string Name { get; set; } 
    public bool Visible { get; set; } 
    public string Width { get; set; } 
} 

Je veux créer un List<UiGridColumn> pour chaque champ avec seulement les valeurs fournies (je ne veux pas une valeur nulle pour les propriétés ignorées).

Est-il possible de créer un List<UiGridColumn> où chaque article List a seulement les valeurs fournies? (Je crains que ce ne soit pas possible, mais pensais que je demanderais) Si oui, comment?

Sinon, mon deuxième choix serait un tableau de chaînes comme ceci:

[{"name":"fullName","visible":false,"width":"90"},{"name":"weekStart","cellFilter":"date"}] 

Je préférerais ne pas en boucle à travers chaque property et attribute et argument pour créer manuellement la chaîne JSON désirée, mais je n » J'ai été capable de trouver un moyen facile de le faire autrement.

public List<Object> GetUiGridColumnDef(string className) 
{ 
    Assembly assembly = typeof(DynamicReportService).Assembly; 
    var type = assembly.GetType(className); 
    var properties = type.GetProperties(); 

    var columnDefs = new List<object>(); 
    foreach (var property in properties) 
    { 
     var column = new Dictionary<string, Object>(); 
     var attributes = property.CustomAttributes; 
     foreach (var attribute in attributes) 
     { 
      if (attribute.AttributeType.Name != typeof(UiGridColumn).Name || attribute.NamedArguments == null) 
        continue; 
      foreach (var argument in attribute.NamedArguments) 
      { 
       column.Add(argument.MemberName, argument.TypedValue.Value); 
      } 
     } 
     columnDefs.Add(column); 
    } 
    return columnDefs; 
} 

Y a-t-il une meilleure façon de procéder?

Répondre

1

Si je comprends bien votre question, vous voulez sérialiser la liste des attributs appliqués aux propriétés d'une classe?

Si oui, vous pouvez faire une méthode d'aide pour le faire comme ceci:

public static string SerializeAppliedPropertyAttributes<T>(Type targetClass) where T : Attribute 
{ 
    var attributes = targetClass.GetProperties() 
           .SelectMany(p => p.GetCustomAttributes<T>()) 
           .ToList(); 

    JsonSerializerSettings settings = new JsonSerializerSettings 
    { 
     NullValueHandling = NullValueHandling.Ignore, 
     Formatting = Formatting.Indented 
    }; 

    return JsonConvert.SerializeObject(attributes, settings); 
} 

utiliser ensuite comme ceci:

string json = SerializeAppliedPropertyAttributes<UiGridColumn>(typeof(PayrollReport)); 

Vous terminerez avec cette sortie, ce qui est assez proche de ce que vous cherchez:

[ 
    { 
    "Name": "fullName", 
    "Visible": false, 
    "Width": "90", 
    "TypeId": "UiGridColumn, JsonTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
    }, 
    { 
    "CellFilter": "date", 
    "Name": "weekStart", 
    "Visible": false, 
    "TypeId": "UiGridColumn, JsonTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" 
    } 
] 

vous remarquerez la propriété TypeId de la base Attribute classe est inclus, et aussi les noms de propriété ne sont pas camel cased. Pour résoudre ce problème, vous aurez besoin d'utiliser un résolveur contrat personnalisé:

public class SuppressAttributeTypeIdResolver : CamelCasePropertyNamesContractResolver 
{ 
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 
    { 
     JsonProperty prop = base.CreateProperty(member, memberSerialization); 
     if (member.DeclaringType == typeof(Attribute) && member.Name == "TypeId") 
     { 
      prop.ShouldSerialize = obj => false; 
     } 
     return prop; 
    } 
} 

Ajouter le résolveur aux paramètres de sérialisation dans la méthode d'assistance et vous devriez être bon d'aller:

JsonSerializerSettings settings = new JsonSerializerSettings 
    { 
     NullValueHandling = NullValueHandling.Ignore, 
     ContractResolver = new SuppressAttributeTypeIdResolver(), 
     Formatting = Formatting.Indented 
    }; 

Maintenant, la sortie devrait ressembler à ceci:

[ 
    { 
    "name": "fullName", 
    "visible": false, 
    "width": "90" 
    }, 
    { 
    "cellFilter": "date", 
    "name": "weekStart", 
    "visible": false 
    } 
] 

violon de démonstration: https://dotnetfiddle.net/2R5Zyi

+0

Ceci est presque parfait. 'Visible' a été défini pour une seule propriété, mais il apparaît dans les deux car il s'agit d'un' bool'. Lorsque j'essaie de le rendre 'nullable', j'obtiens' 'Visible' n'est pas un argument d'attribut nommé valide car ce n'est pas un type de paramètre d'attribut valide'. Suggestions? – davids

+0

Vous pouvez utiliser 'DefaultValueHandling = DefaultValueHandling.Ignore' dans les paramètres pour supprimer' Visible' lorsqu'il est défini sur sa valeur par défaut 'false'. Cependant, cela le supprimera également pour le premier objet, où vous l'avez explicitement défini sur false. Malheureusement, avec cette solution, vous ne pouvez pas l'avoir dans les deux sens: soit vous incluez toujours la valeur, soit vous l'incluez uniquement lorsqu'elle est différente de la valeur par défaut. –

+0

Bon à savoir. Je suis d'accord avec les valeurs par défaut se sauté. Cependant, je veux que 'Visible' soit' true' par défaut, donc j'ai changé la valeur par défaut et ajouté le code 'DefaultValueHandling'. Cela fonctionne exactement comme prévu. Merci! – davids