首页 > 解决方案 > 在引用多个库时简化方法上的属性装饰器

问题描述

这是一个小小的不便,但最终会生成大量样板代码。我正在使用多个库(ServiceStack.Net、Json.Net、DataContractSerializer 等),并强制所有可能的解释器正确序列化/反序列化我的对象,我最终得到了许多如下所示的属性定义:

private const string select_comparisons = "select-comparisons";
[DataMember(Name = select_comparisons, EmitDefaultValue = true)]
[ApiMember(Name = select_comparisons)]
[JsonProperty(select_comparisons, DefaultValueHandling = DefaultValueHandling.Populate, NullValueHandling = NullValueHandling.Include)]
public bool SelectComparisons { get; set; }

这很烦人。最好编写如下内容:

[MyAttributeToRuleThemAll("select-comparisons", EmitDefaultValue = true, IgnoreNulls = true)]
public bool SelectComparisons { get; set; }

哪里MyAttributeToRuleThemAll看起来像这样:

public class MyAttributeToRuleThemAll : Attribute, DataMember, ApiMember, JsonProperty 
{
    //insert attribute-specific logic in the constructor
}

我意识到建议的解决方案是不可能的,因为您不能以这种方式继承,但似乎应该有某种方法将公共属性简化为单个可重用组件。

更新:

我尝试使用以下代码从引用的副本中使用答案。

[TypeDescriptionProvider(typeof(SerializableTypeDescriptionProvider))]
public class SerializableTest
{
    [Serializable("nu-id", DefaultValue = 2)]
    public int Id { get; set; }

    [Serializable("nu-key", DefaultValue = "2")]
    public string Key { get; set; }

}

public interface IMetadatAttribute
{
    Attribute[] Process();
}

public enum DefaultValueOptions
{
    Include,
    Exclude,
    IncludeAndPopulate
}

public class SerializableAttribute : Attribute, IMetadatAttribute
{
    public DefaultValueHandling DefaultValueHandling { get; set; }
    public DefaultValueOptions DefaultValueOptions { get; set; }
    public bool EmitDefaultValue { get; set; }
    public bool IsRequired { get; set; }
    public bool ExcludeInSchema { get; set; }
    public bool AllowMultiple { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public string DataType { get; set; }
    public string Format { get; set; }
    public string ParameterType { get; set; }
    public string Route { get; set; }
    public string Verb { get; set; }
    public int Order { get; set; }
    public object DefaultValue { get; set; }

    public SerializableAttribute()
    {
        this.DefaultValueHandling = DefaultValueHandling.Include;
    }

    public SerializableAttribute(string name) : this()
    {
        Name = name;
    }

    public SerializableAttribute(string name, object defaultValue, bool emitDefaultValue, string description) : this(name)
    {
        DefaultValue = defaultValue;
        EmitDefaultValue = emitDefaultValue;
        Description = description;
    }

    public Attribute[] Process()
    {
        var attributes = new Attribute[]{
            new DataMemberAttribute() {
                EmitDefaultValue = EmitDefaultValue,
                IsRequired = IsRequired,
                Name = Name,
                Order = Order },
            new ApiMemberAttribute() {
                Name = Name,
                Description = Description,
                ExcludeInSchema = ExcludeInSchema,
                IsRequired = IsRequired,
                AllowMultiple = AllowMultiple,
                DataType = DataType,
                Format = Format,
                ParameterType = ParameterType,
                Route = Route,
                Verb = Verb
            },
            new JsonPropertyAttribute(Name) {
                DefaultValueHandling = DefaultValueHandling,
                PropertyName = Name,
                NullValueHandling = NullValueHandling.Ignore,
                ObjectCreationHandling = ObjectCreationHandling.Reuse
            },
            new DefaultValueAttribute(DefaultValue) {}
        };
        return attributes;
    }
}

public class SerializableDescriptor : PropertyDescriptor
{
    PropertyDescriptor original;
    public SerializableDescriptor(PropertyDescriptor originalProperty)
        : base(originalProperty) => original = originalProperty;
    public override AttributeCollection Attributes
    {
        get
        {
            var attributes = base.Attributes.Cast<Attribute>();
            var result = new List<Attribute>();
            foreach (var item in attributes)
            {
                if (item is IMetadatAttribute)
                {
                    var attrs = ((IMetadatAttribute)item).Process();
                    if (attrs != null)
                    {
                        foreach (var a in attrs)
                            result.Add(a);
                    }
                }
                else
                    result.Add(item);
            }
            return new AttributeCollection(result.ToArray());
        }
    }

    public override Type ComponentType => original.ComponentType;
    public override bool IsReadOnly => original.IsReadOnly;
    public override Type PropertyType => original.PropertyType;

    public override bool CanResetValue(object component) => original.CanResetValue(component);

    public override object GetValue(object component)
    {
        return original.GetValue(component);
    }
    public override void ResetValue(object component) => original.ResetValue(component);
    public override void SetValue(object component, object value) => original.SetValue(component, value);
    public override bool ShouldSerializeValue(object component) => original.ShouldSerializeValue(component);
}

public class SerializableTypeDescriptor : CustomTypeDescriptor
{
    ICustomTypeDescriptor original;
    public SerializableTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
        : base(originalDescriptor) => original = originalDescriptor;
    public override PropertyDescriptorCollection GetProperties() => GetProperties(new Attribute[] { });
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
            .Select(p => new SerializableDescriptor(p))
            .ToArray();
        return new PropertyDescriptorCollection(properties);
    }
}

public class SerializableTypeDescriptionProvider : TypeDescriptionProvider
{
    public SerializableTypeDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(object))) { }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
        object instance)
    {
        var baseDescriptor = base.GetTypeDescriptor(objectType, instance);
        return new SerializableTypeDescriptor(baseDescriptor);
    }
}

您可以执行以下操作:

void Main()
{
    var nuTest = new SerializableTest();
    var jsonOut = JsonConvert.SerializeObject(nuTest).Dump(); //or insert whatever ToJson/ToString logic here that you want
    //expecting: { "nu-id": 2, "nu-key": "2" }
    //getting: { "Id": 0, "Key": "null" }
}

虽然这编译和执行没有错误,但指定的属性没有按预期返回。手动注入[JsonProperty("nu-name")]属性按预期工作。我假设这个属性方法是在编译时而不是在运行时生成的?

标签: c#attributesjson.netservicestack

解决方案


推荐阅读