提问人:SRNissen 提问时间:4/11/2023 更新时间:4/11/2023 访问量:74
有没有一种通用方法可以递归检查反序列化对象是否存在空字段?
Is there a generic way to recursively check a deserialized object for null fields?
问:
我收到许多不同的消息,我将它们反序列化为非常不同的对象
这些对象中的大多数都具有不可为 null 的字段,但在所有消息类型(从 GraphQL、Azure 服务和存储总线,到 CosmosSB 以及我可能忘记的更多)中,其中一些对象很乐意反序列化为具有 null 字段的对象。
因此,我非常可以使用一个可以让我做的组件
T objectFromMessage = GetObject(message);
FieldAsserter.AssertAllFieldsSet<T>(objectFromMessage); //Throws if any non-nullable fields are null
您是否知道默认情况下或Nuget中的某个位置是否已经存在任何此类字段验证方法?或者,如果我必须自己写,你能给我任何线索吗?从哪里开始寻找?
你尝试了什么,你期待什么?
在这一点上,真的没什么 - 粗略搜索 NuGet 和一些关于反射的想法是我在思考“也许 SO 有答案”之前得到的。
SO 上已经有几个类似的问题,但它们似乎适用于单个对象 - 答案是“将扩展方法编写到您的类中”,但我想要一个适用于任何事情的通用方法 - 我有太多的消息类型。
答:
您是否正在寻找像下面这样简单的东西?您还可以添加自定义或默认属性,以指示在此检查中忽略属性。
public static bool AreAllPropertiesNotNull<T>(this T obj) where T : class
{
PropertyInfo[] properties = typeof(T).GetProperties();
foreach (PropertyInfo property in properties)
{
if (property.GetCustomAttribute<IgnoreAttribute>() != null)
{
continue; // Skip properties with Ignore attribute
}
dynamic propertyValue = property.GetValue(obj);
if (propertyValue == null)
{
return false;
}
}
return true;
}
使用扩展方法。
T objectFromMessage = GetObject(message);
Assert.True(objectFromMessage.AreAllPropertiesNotNull());
评论
或者,如果您只想检查不可为空的公共属性(例如 而不是 ),您可以过滤掉可为 null 的属性,并仅检查不可为 null 的属性,如下所示:string
string?
(注意:这需要 .NET 6.0 或更高版本。
public static void RequiresPropertiesNotNull(object item, string name)
{
if (item is null)
throw new ArgumentNullException(nameof(item), "Item being checked cannot be null.");
name ??= "<Unknown>"; // Don't want to throw if name is null, because it would hide the thing we're checking for nulls.
var allProps = item.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.FlattenHierarchy);
var nonNullableProps =
from prop in allProps
where
prop.CanRead
&& !prop.PropertyType.IsValueType
&& !IsNullable(prop)
select prop;
foreach (var prop in nonNullableProps)
{
object? value = prop.GetValue(item);
if (value is null)
throw new InvalidOperationException($"Property {prop.Name} for {name} of type {item.GetType().FullName} cannot be null.");
RequiresPropertiesNotNull(value, name);
}
}
public static bool IsNullable(PropertyInfo prop)
{
var context = new NullabilityInfoContext();
var info = context.Create(prop);
return info.WriteState is NullabilityState.Nullable;
}
请注意,这是递归的,因此它将检查所有公共属性和(递归)它们的所有公共属性,依此类推。
它不检查自引用对象,因此如果您尝试使用它来检查包含循环的对象图,您将收到堆栈溢出错误。
您可以按如下方式使用它:给定类和类,其中类包含允许为 null 的属性和不允许为 null 的属性:Outer
Inner
Inner
CanBeNull
InnerName
public sealed class Outer
{
public string OuterName { get; set; }
public Inner Inner { get; set; }
}
public sealed class Inner
{
public string InnerName { get; set; }
public string? CanBeNull { get; set; }
}
然后在下面的代码中,第二次调用 将引发异常:RequiresPropertiesNotNull()
public static void Main()
{
var innerOk = new Inner { InnerName = "InnerOK" };
var innerBad = new Inner();
var outerOk = new Outer { OuterName = "OuterOK", Inner = innerOk };
var outerBad = new Outer { OuterName = "OuterBad", Inner = innerBad };
RequiresPropertiesNotNull(outerOk, nameof(outerOk)); // OK
RequiresPropertiesNotNull(outerBad, nameof(outerBad)); // Throws
}
如果要删除递归部分,只需删除实现中的代码行即可。RequiresPropertiesNotNull(value, name);
RequiresPropertiesNotNull()
评论
DateTime.Now
null