如何从 REST 控制器上的“IEnumerable<T>”动态删除默认对象

问题描述 投票:0回答:1

我有一个接收类的 Rest 端点

EntityInsertRequest

    public class EntityInsertRequest
    {
        public string Description { get; set; }
        public IEnumerable<EntityFieldInsertRequest> Fields { get; set; }
    }

    public class EntityFieldInsertRequest
    {
        public string DatabaseField { get; set; }
        public string Description { get; set; }
    }

我如何在对象的初始化中进行过滤或覆盖以删除列表的默认对象,在这种情况下,列表的默认对象将是:

{
    "databaseField": "",
    "description": ""
}

我会发送

{
    "description": "FooBar",
    "fields": [
        {
            "databaseField": "",
            "description": ""
        },
        {
            "databaseField": "Foo",
            "description": "Bar"
        }
    ]
}

我希望用空列表实例化它,并为其他端点动态复制。

我无法更改发送方的发送方式并删除发送方的对象,它需要位于后端。

我尝试创建一个 JsonConverter

public class NullableIEnumerableOverride<T> : JsonConverter<IEnumerable<T>> where T : class
{
    public override bool CanConvert(Type typeToConvert)
    {
        return typeof(System.Collections.IEnumerable).IsAssignableFrom(typeToConvert) && typeToConvert != typeof(string) && typeToConvert.GenericTypeArguments[0].IsClass;
    }

    public override IEnumerable<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var internalOptions = new JsonSerializerOptions(options);
     
        var list = JsonSerializer.Deserialize<IEnumerable<T>>(ref reader);

        if (list == null || !list.Any())
        {
            return  (IEnumerable<T>)CreateEmptyInstance(typeToConvert);
        }


        //cant get properties of generic object with typeof(T).GetProperties()
        //bool allFieldsAreDefault = list.All(item =>
        //{
        //   var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        //    return properties.All(prop =>
        //    {
        //        var value = prop.GetValue(item);
        //        var defaultValue = GetDefault(prop.PropertyType);
        //        return Equals(value, defaultValue);
        //    });
        //});
        //return allFieldsAreDefault ? (IEnumerable<T>)CreateEmptyInstance(typeToConvert) : list;

        return list;
    }

    private static object CreateEmptyInstance(Type typeToConvert)
    {
        if (typeToConvert.IsGenericType && typeof(IEnumerable<>).IsAssignableFrom(typeToConvert.GetGenericTypeDefinition()))
        {
            Type elementType = typeToConvert.GetGenericArguments()[0];
            Type listType = typeof(List<>).MakeGenericType(elementType);
            return Activator.CreateInstance(listType) ?? throw new InvalidOperationException($"Unable to create instance of type {listType}.");
        }

        throw new InvalidOperationException($"Type {typeToConvert} is not supported.");
    }

    public override void Write(Utf8JsonWriter writer, IEnumerable<T>? value, JsonSerializerOptions options)
    {
        if (value == null)
        {
            writer.WriteNullValue();
            return;
        }

        JsonSerializerOptions jsonOptions = new()
        {
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        };

        JsonSerializer.Serialize(writer, value, jsonOptions);
    }

    private object? GetDefault(Type type)
    {
        return type.IsValueType ? Activator.CreateInstance(type) : null;
    }

}

并且在

program.cs

builder.Services.AddControllers().AddJsonOptions(options =>
{
    options.JsonSerializerOptions.Converters.Add(new NullableIEnumerableOverride<object>());
});

并且因为我想在其他端点上使用它并将其设置为

object
上的通用
program.cs
,所以我收到此转换错误:

System.InvalidCastException: Unable to cast object of type 'System.Collections.Generic.List`1[System.Object]' to type 'System.Collections.Generic.IEnumerable`1[Logic.Request.Entity.EntityFieldInsertRequest]'.
   at System.Text.Json.ThrowHelper.ThrowInvalidCastException_DeserializeUnableToAssignValue(Type typeOfValue, Type declaredType)
   at System.Text.Json.JsonSerializer.<UnboxOnRead>g__ThrowUnableToCastValue|50_0[T](Object value)
   at System.Text.Json.JsonSerializer.UnboxOnRead[T](Object value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.Metadata.JsonPropertyInfo`1.ReadJsonAndSetMember(Object obj, ReadStack& state, Utf8JsonReader& reader)
   at System.Text.Json.Serialization.Converters.ObjectDefaultConverter`1.OnTryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value)
   at System.Text.Json.Serialization.JsonConverter`1.TryRead(Utf8JsonReader& reader, Type typeToConvert, JsonSerializerOptions options, ReadStack& state, T& value, Boolean& isPopulatedValue)
   at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.ContinueDeserialize(ReadBufferState& bufferState, JsonReaderState& jsonReaderState, ReadStack& readStack)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsync(Stream utf8Json, CancellationToken cancellationToken)
   at System.Text.Json.Serialization.Metadata.JsonTypeInfo`1.DeserializeAsObjectAsync(Stream utf8Json, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
   at Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonInputFormatter.ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
   at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinder.BindModelAsync(ModelBindingContext bindingContext)
   at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value, Object container)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
   at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
   at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
c# json arraylist controller overriding
1个回答
0
投票

要从 .NET 控制器的 IEnumerable 列表中动态删除默认对象,您可以在进一步处理之前修改 EntityInsertRequest。这可以在模型绑定器中或直接在控制器操作中完成。

在控制器内使用 LINQ 过滤掉默认对象: 过滤字段:在保存或进一步处理请求之前,过滤字段以删除任何默认或空条目。

[HttpPost]
public IActionResult InsertEntity([FromBody] EntityInsertRequest request)
{
    // Remove default/empty objects from the Fields list
    request.Fields = request.Fields?.Where(field => 
        !(string.IsNullOrWhiteSpace(field.DatabaseField) && string.IsNullOrWhiteSpace(field.Description))
    ).ToList() ?? new List<EntityFieldInsertRequest>();

    // Continue processing the cleaned request
    // Your save or business logic here

    return Ok();
}

此方法可确保 DatabaseField 和 Description 均为空或 null 的任何 EntityFieldInsertRequest 对象将从 Fields 集合中删除。 如果Fields集合为null,它将初始化一个空列表以避免空引用异常。

© www.soinside.com 2019 - 2024. All rights reserved.