C# System.Text.Json源生成器方法 C#如何使用Source Generation模式提升JSON性能

3次阅读

system.text.json源生成器启用失败的常见原因包括未启用implicitusings和NULLable、未引用正确版本包、未正确定义partial jsonserializercontext类及[jsonserializable]特性,且必须使用生成的上下文实例调用serialize/deserialize方法。

C# System.Text.Json源生成器方法 C#如何使用Source Generation模式提升JSON性能

System.Text.Json源生成器启用失败的常见原因

源生成器在 .NET 6+ 中默认不启用,即使项目 SDK 是 microsoft.NET.Sdk,也必须显式开启源生成支持。最常被忽略的是未在 .csproj 中设置 ImplicitUsingsNullable,或未引用正确的包版本。

  • System.Text.Json 源生成依赖 System.Text.Json.SourceGeneration 包(.NET 7+ 内置,但需确认 SDK 版本;.NET 6 需手动安装 System.Text.Json 6.0.3+)
  • 项目必须启用 <implicitusings>enable</implicitusings>,否则 JsonSerializerContext 相关类型无法解析
  • 若使用 partial 类型定义上下文,类名必须以 JsonSerializerContext 结尾,且标记 [JsonSerializable] —— 否则生成器不触发
  • 生成器输出在 obj/Debug/netX.X/ 下的 GeneratedSources/ 文件夹中,若该目录为空,说明生成未执行,可检查 dotnet build -v:d 日志中是否有 SourceGenerator 相关警告

如何正确定义 JsonSourceGenerationContext

源生成器不是自动扫描所有类型,而是基于你声明的 JsonSerializerContext 子类,显式列出需要序列化的类型。这个类必须是 partial,且每个 [JsonSerializable] 属性对应一个根类型(支持嵌套、泛型接口,但不支持 Object 或未标注的运行时类型)。

[JsonSerializable(typeof(Person))] [JsonSerializable(typeof(List<Person>))] [JsonSerializable(typeof(Dictionary<string, Order>))] internal partial class AppJsonContext : JsonSerializerContext { }
  • PersonOrder 类本身不需要额外标记,但字段/属性访问必须为 public,或通过 [JsonInclude] 开放 private 成员
  • 若类型含 DateTimeOffsetGuid 等,无需额外配置,默认已优化;但自定义转换器(如 JsonConverter<t></t>)仍需在上下文中注册:[JsonSerializable(typeof(Person), TypeInfoPropertyName = "PersonInfo")] + 手动实现 PersonInfo 属性
  • 不支持动态类型(如 ExpandoObject)、dynamic、未闭合泛型(List)—— 这些会跳过生成,回退到反射路径

序列化/反序列化时如何正确使用生成的上下文

启用源生成后,不能继续用静态 JsonSerializer.Serialize<t>(value)</t>,那仍走反射路径。必须传入生成的 AppJsonContext.default 实例,并使用其 Serialize/Deserialize 方法,才能命中编译期生成的代码路径。

var context = AppJsonContext.Default; string json = context.Serialize(person); // ✅ 调用生成的代码 Person p = context.Deserialize<Person>(json); // ✅  // ❌ 以下仍走反射,完全绕过源生成 string json2 = JsonSerializer.Serialize(person); Person p2 = JsonSerializer.Deserialize<Person>(json2);
  • AppJsonContext.Default线程安全的单例,可全局复用,无需缓存或池化
  • 若需不同选项(如 PropertyNamingPolicy),必须定义多个 partial 上下文类,每个带独立 [JsonSourceGenerationOptions] 特性;无法在运行时切换同一上下文的选项
  • 生成代码对 null 值处理与反射路径一致,但字段级 JsonIgnore 必须在类型定义时存在,运行时通过 JsonSerializerOptions 添加的忽略规则无效

性能差异在哪?什么场景下值得上源生成

源生成主要消除反射调用和运行时类型检查开销,在高频、小对象、固定结构的 JSON 场景下收益明显(如微服务间 DTO 通信、API 响应序列化)。但它的优势不是“绝对更快”,而是“更稳定”——避免 JIT 编译抖动和 GC 压力波动。

  • 典型提升:10–30% 吞吐量(Serialize),反序列化可达 2× 以上(尤其含集合或深层嵌套时)
  • 真正关键收益是首次调用无 JIT 延迟:反射路径首次 Serialize<t></t> 可能卡住 5–20ms;源生成代码在编译时就产出 IL,启动即可用
  • 不适合场景:类型结构高度动态(如配置驱动的 JSON Schema)、开发期频繁改模型(每次改都要 rebuild 才能更新生成代码)、或仅偶尔序列化大文件(此时 IO 或压缩才是瓶颈)
  • 调试时注意:生成的代码在 obj/ 下,VS 默认不加载,需在调试设置中启用 “Enable source server support” 并勾选 “Enable Just My Code” 外的符号加载,否则断点进不到生成方法里

text=ZqhQzanResources