5

I have the following code located in a Swashbuckle.Swagger.IOperationFilter class.

List<CustomAttributeData> controllerAttributes = apiDescription
    .ActionDescriptor
    .ControllerDescriptor
    .ControllerType
    .CustomAttributes
    .Where(a => a.AttributeType == typeof(SwaggerFileInFormDataAttribute))
    .ToList();

I am obtaining the collection successfully. I want to convert the collection List<CustomAttributeData> into a collection of my custom attribute List<SwaggerFileInFormDataAttribute>.

enter image description here

I want to convert the System.Reflection.CustomAttributeData class to my custom attribute SwaggerFileInFormDataAttribute.


Edit 1:
Here is how my SwaggerFileInFormDataAttribute attribute looks like.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
public class SwaggerFileInFormDataAttribute : Attribute
{
    private ICollection<string> displayNames;

    public SwaggerFileInFormDataAttribute()
    {
        this.DisplayNames = new List<string> { "File" };
    }

    public SwaggerFileInFormDataAttribute(params string[] displayNames)
    {
        this.DisplayNames = displayNames;
    }

    public ICollection<string> DisplayNames
    {
        get
        {
            if (this.displayNames == null)
                return new List<string> { "File" };
            else
                return this.displayNames;
        }
        set => displayNames = value;
    }
}

After successfully converting the List<CustomAttributeData> to List<SwaggerFileInFormDataAttribute> I'll be using the property shown in the class SwaggerFileInFormDataAttribute named DisplayNames.

G.Dimov
  • 2,173
  • 3
  • 15
  • 40

2 Answers2

4

First solution:

List<SwaggerFileInFormDataAttribute> customControllerAttributes = apiDescription
    .ActionDescriptor
    .ControllerDescriptor
    .ControllerType
    .CustomAttributes
    .Where(a => a.AttributeType == typeof(SwaggerFileInFormDataAttribute))
    .Select(a =>
    {
        ReadOnlyCollection<CustomAttributeTypedArgument> costuctorArgumentsReadOnlyCollection = (ReadOnlyCollection<CustomAttributeTypedArgument>)a.ConstructorArguments.Select(x => x.Value).FirstOrDefault();

        string[] contructorParams = costuctorArgumentsReadOnlyCollection?.ToArray().Select(x => x.Value.ToString()).ToArray();
        SwaggerFileInFormDataAttribute myCustomAttr = (SwaggerFileInFormDataAttribute)Activator.CreateInstance(a.AttributeType, contructorParams);
        return myCustomAttr;
    })
    .ToList();

Explanation: Both variables costuctorArgumentsReadOnlyCollection and contructorParams can be skipped in case the desired attribute has an empty constructor.

In that case, only this line will suffice:
(SwaggerFileInFormDataAttribute)Activator.CreateInstance(a.AttributeType, null);

Second solution:

This is a much cleaner solution. We can completely skip the CustomAttributeData class in the IOperationFilter like that:

List<SwaggerFileInFormDataAttribute> controllerAttributes = apiDescription
    .ActionDescriptor
    .ControllerDescriptor
    .GetCustomAttributes<SwaggerFileInFormDataAttribute>()
    .ToList();
G.Dimov
  • 2,173
  • 3
  • 15
  • 40
2

After your .Where, add a select and translate as appropriate. The easiest would be, in your custom class, create a constructor that takes a SwaggerFileInFormDataAttribute as the parameter and maps to your custom class as needed. Then your Select would be:

.Where(a => a.AttributeType == typeof(SwaggerFileInFormDataAttribute))
.Select(a => new CustomAttributeData(a))
.ToList();
Jonathan
  • 4,916
  • 2
  • 20
  • 37
  • This doesn't work, my custom attribute named `SwaggerFileInFormDataAttribute` doesn't contain such constructor., where you can pass a `CustomAttributeData` parameter. This line `.Select(a => new CustomAttributeData(a))` doesn't work – G.Dimov Aug 26 '20 at 07:44
  • I think you might have changed your question, or it is worded awkwardly. Are you translating from an existing type into a list of your own custom type, or vice versa? If you are converting into a list of your own custom type, add the constructor method. If vice versa, either create a method to assist with the conversion, or do it when you new up the class in your `select` (eg `a=>new Existing{Title = a.Title, Status = a.Status}` etc – Jonathan Aug 26 '20 at 18:58