我有一个接收类的 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)
要从 .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,它将初始化一个空列表以避免空引用异常。