通过反射获取类型反序列化Service Bus JSON消息遇空值求助
问题描述
从Service Bus接收JSON格式消息,消息结构包含JobType、JobArgsType和JobArgs字段。最初尝试通过反射获取JobArgsType指定的类型(如DispatcherWeb.Infrastructure.BackgroundJobs.ImportJobArgs)反序列化JobArgs时,出现Cannot resolve symbol 'className'错误;改用非泛型的JsonSerializer.Deserialize方法后,Type.GetType(messageObject.JobArgsType)返回null,无法获取目标类型。
初始代码
private static async Task ReceiveMessage(string serviceBusQueueName, ServiceBusClient client) { var processor = client.CreateProcessor(serviceBusQueueName, new ServiceBusProcessorOptions()); processor.ProcessMessageAsync += async (messageArgs) => { var messageObject = JsonSerializer.Deserialize<MessageObject>(messageArgs.Message?.Body.ToString()); var className = Type.GetType(messageObject.JobArgsType); var data = JsonSerializer.Deserialize<className>(messageObject.JobArgs.ToString()); await messageArgs.CompleteMessageAsync(messageArgs.Message); }; processor.ProcessErrorAsync += async (messageArgs) => { Console.WriteLine(messageArgs.Exception.Message); }; await processor.StartProcessingAsync(); } internal class MessageObject { public string JobType { get; set; } public string JobArgsType { get; set; } public object JobArgs { get; set; } }
修改后代码
processor.ProcessMessageAsync += async (messageArgs) => { var messageObject = JsonSerializer.Deserialize<MessageObject>(messageArgs.Message?.Body.ToString()); var className = Type.GetType(messageObject.JobArgsType); var data = JsonSerializer.Deserialize(messageObject.JobArgs.ToString(), className); await messageArgs.CompleteMessageAsync(messageArgs.Message); };
错误原因
Type.GetType(string)方法仅在传入完全限定类型名(命名空间+类名)+程序集名称时,才能跨程序集正确找到类型。如果仅传入命名空间加类名,只有当目标类型位于当前执行程序集或系统核心程序集(如mscorlib)时才有效。你的ImportJobArgs大概率在其他自定义程序集中,因此只传DispatcherWeb.Infrastructure.BackgroundJobs.ImportJobArgs会导致返回null。
另外,初始代码中的Cannot resolve symbol 'className'错误,是因为泛型方法的类型参数需要编译时常量,不能用变量作为泛型参数,改用非泛型版Deserialize的方向是正确的。
解决方案
1. 补充程序集信息到JobArgsType字段
发送消息时,将类型的完全限定名+程序集名称传入JobArgsType,格式示例:
DispatcherWeb.Infrastructure.BackgroundJobs.ImportJobArgs, DispatcherWeb
(其中DispatcherWeb是目标类型所在的程序集名称)
修改后Type.GetType就能直接正确解析类型。
2. 手动指定程序集查找类型
如果无法修改发送端的JobArgsType内容,可以通过已知类型定位程序集,再从中查找目标类型:
// 方式1:通过同程序集的已知类型获取程序集 var assembly = typeof(SomeTypeInSameAssembly).Assembly; // 方式2:直接加载目标程序集(需确保程序集已被加载) // var assembly = Assembly.Load("DispatcherWeb"); // 从程序集中查找类型 var targetType = assembly.GetType(messageObject.JobArgsType); if (targetType != null) { var data = JsonSerializer.Deserialize(messageObject.JobArgs.ToString(), targetType); // 后续业务处理 }
注:SomeTypeInSameAssembly是与ImportJobArgs位于同一程序集的任意类型。
3. 维护类型映射字典
如果涉及的JobArgs类型数量有限,可提前维护一个类型映射字典,避免反射查找的不确定性:
// 提前初始化类型映射 private static readonly Dictionary<string, Type> _jobArgTypeMap = new Dictionary<string, Type> { { "DispatcherWeb.Infrastructure.BackgroundJobs.ImportJobArgs", typeof(DispatcherWeb.Infrastructure.BackgroundJobs.ImportJobArgs) }, // 其他需要处理的类型依次添加 }; // 接收消息时使用字典获取类型 processor.ProcessMessageAsync += async (messageArgs) => { var messageObject = JsonSerializer.Deserialize<MessageObject>(messageArgs.Message?.Body.ToString()); if (_jobArgTypeMap.TryGetValue(messageObject.JobArgsType, out var targetType)) { var data = JsonSerializer.Deserialize(messageObject.JobArgs.ToString(), targetType); // 处理业务逻辑 } await messageArgs.CompleteMessageAsync(messageArgs.Message); };
内容的提问来源于stack exchange,提问作者Eugene Sukh

