如何在System.Text.Json中使用Reflection.Emit.ModuleBuilder反序列化JSON文档
我完全理解你现在的困扰——之前用Newtonsoft.Json配合动态生成的Response_T类型反序列化大量数据时速度飞快,结果迁移到System.Text.Json(.NET Framework 4.8用的9.0.6 NuGet包)后,虽然能成功实例化动态类型,但所有字段全是null,这确实让人挠头。咱们来一步步解决这个问题:
问题根源
System.Text.Json和Newtonsoft.Json的默认行为有个关键差异:System.Text.Json默认只序列化/反序列化公共属性(Property),而你通过Reflection.Emit创建的动态类型用的是公共字段(Field)。这就是为什么所有字段都无法被正确赋值的核心原因。另外,System.Text.Json默认是区分JSON键和成员名称大小写的,这也可能是潜在的问题点。
解决方案
1. 配置JsonSerializerOptions,启用字段支持
首先要修改序列化配置,让System.Text.Json能够识别并处理公共字段:
var options = new JsonSerializerOptions { // 关键:开启字段的序列化/反序列化支持 IncludeFields = true, // 如果你的JSON键是驼峰命名(比如"status")而字段是 PascalCase(比如"Status"),启用这个选项忽略大小写 PropertyNameCaseInsensitive = true };
2. 修改反序列化代码(优化异步调用)
另外,建议用await替代.Result来调用异步方法,避免.NET Framework环境下可能出现的死锁问题。修改后的反序列化逻辑如下:
Response response = new Response(); Type dynamicResponseType = ResponseBuilder.CreateResponseType<T>(); using (var stream = await httpresponse.Content.ReadAsStreamAsync()) { // 传入配置好的options,反序列化到动态类型 var x = await JsonSerializer.DeserializeAsync(stream, dynamicResponseType, options); // 读取字段值的逻辑和之前保持一致 FieldInfo fi = dynamicResponseType.GetField("Status"); response.Status = (Response.ResponseStatus)fi.GetValue(x); fi = dynamicResponseType.GetField("ErrorMessage"); response.ErrorMessage = (string)fi.GetValue(x); fi = dynamicResponseType.GetField("ErrorDetails"); response.ErrorDetails = (string)fi.GetValue(x); fi = dynamicResponseType.GetField("Logon"); response.Logon = (Logon)fi.GetValue(x); fi = dynamicResponseType.GetField("Object"); response.Object = fi.GetValue(x); }
3. (可选)为动态字段添加JsonPropertyName特性(精准映射)
如果你的JSON键和动态字段名称完全不匹配(比如JSON里是"logon"而字段是"Logon",且不想启用大小写不敏感),可以在创建动态字段时手动添加JsonPropertyNameAttribute来做精准映射。修改ResponseBuilder.CreateResponseType<T>中的字段创建逻辑:
// 以Logon字段为例 fieldBuilder = typeBuilder.DefineField("Logon", typeof(Data.Logon), FieldAttributes.Public); fieldBuilder.SetOffset(fieldOffset); fieldOffset += IntPtr.Size; // 添加JsonPropertyName特性,指定JSON中的键名 var jsonAttrType = typeof(System.Text.Json.Serialization.JsonPropertyNameAttribute); var attrConstructor = jsonAttrType.GetConstructor(new[] { typeof(string) }); var attrBuilder = new CustomAttributeBuilder(attrConstructor, new object[] { "logon" }); fieldBuilder.SetCustomAttribute(attrBuilder);
验证效果
做完以上修改后,System.Text.Json就能正确识别动态类型的公共字段,并将JSON数据反序列化到对应的字段中了,速度应该能和之前用Newtonsoft.Json时持平。
内容来源于stack exchange

