如何从生成代码触发C# JSON源代码生成器执行?
我实现了一个增量源代码生成器,可生成符合System.Text.Json源代码生成器要求的WeatherJsonContext类,但当前必须执行两次构建才能成功:第一次构建时自定义生成器输出代码,但编译器会报“WeatherJsonContext未实现继承的抽象成员”错误;第二次构建时Json源代码生成器才会执行,最终构建成功。需要实现单次构建完成整个流程。
自定义生成器代码
namespace ClassJsonGen; using System.Diagnostics; using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; [Generator] public class CustomGenerator : IIncrementalGenerator { private const string TestSource = @" namespace WebJsonGen { using System.Text.Json.Serialization; [JsonSerializable(typeof(IEnumerable<WeatherForecast>))] [JsonSerializable(typeof(IEnumerable<StormForecast>))] public partial class WeatherJsonContext : JsonSerializerContext { } }"; public void Initialize(IncrementalGeneratorInitializationContext context) { #if DEBUG if (!Debugger.IsAttached) { Debugger.Launch(); } #endif var todoInput = context.SyntaxProvider .CreateSyntaxProvider( predicate: static (s, _) => s is ClassDeclarationSyntax, transform: static (ctx, _) => ctx.Node) .Where(static m => m is not null); context.RegisterSourceOutput(todoInput, (ctx, inp) => { // TODO use input to determine which [JsonSerializable] we need :-) ctx.AddSource("JSON.g.cs", SourceText.From(TestSource, Encoding.UTF8)); }); } }
生成的代码
namespace WebJsonGen { using System.Text.Json.Serialization; [JsonSerializable(typeof(IEnumerable<WeatherForecast>))] [JsonSerializable(typeof(IEnumerable<StormForecast>))] public partial class WeatherJsonContext : JsonSerializerContext { } }
消费项目的.csproj配置
<PropertyGroup> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <CompilerGeneratedFilesOutputPath>Generated</CompilerGeneratedFilesOutputPath> </PropertyGroup> <ItemGroup> <!-- Exclude the output of source generators from the compilation --> <Compile Remove="$(CompilerGeneratedFilesOutputPath)/System.Text.Json.SourceGeneration/**/*.cs" /> <Compile Include="$(CompilerGeneratedFilesOutputPath)/System.Text.Json.SourceGeneration/**/JSON*.cs" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\ClassJsonGen\ClassJsonGen.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" /> </ItemGroup>
解决方法
问题核心是源代码生成器的执行顺序:自定义生成器与System.Text.Json生成器默认并行执行,Json生成器无法在同一轮构建中识别到刚生成的WeatherJsonContext类,导致第一次构建报错。以下两种方案可实现单次构建完成:
方案1:使用RegisterPostInitializationOutput输出代码
将生成WeatherJsonContext的逻辑移至RegisterPostInitializationOutput方法,该方法会在所有生成器初始化完成、主编译分析前执行,输出的代码会被纳入初始编译流程,让Json生成器在同一轮构建中就能识别到目标类。
修改自定义生成器的Initialize方法:
public void Initialize(IncrementalGeneratorInitializationContext context) { #if DEBUG if (!Debugger.IsAttached) { Debugger.Launch(); } #endif // 改用RegisterPostInitializationOutput输出代码 context.RegisterPostInitializationOutput(ctx => { ctx.AddSource("JSON.g.cs", SourceText.From(TestSource, Encoding.UTF8)); }); // 保留原语法分析逻辑(若需动态生成可后续扩展) var todoInput = context.SyntaxProvider .CreateSyntaxProvider( predicate: static (s, _) => s is ClassDeclarationSyntax, transform: static (ctx, _) => ctx.Node) .Where(static m => m is not null); }
注意:如果生成逻辑需要依赖语法分析结果(如根据项目类动态添加
JsonSerializable),该方案不适用,需采用方案2。
方案2:指定生成器依赖关系
通过[GeneratorDependency]特性明确生成器执行顺序,强制Json生成器在自定义生成器完成输出后再执行。
在自定义生成器类上添加特性:
[Generator] [GeneratorDependency(typeof(System.Text.Json.SourceGeneration.JsonSourceGenerator))] public class CustomGenerator : IIncrementalGenerator { // 原有代码... }
该特性告知Roslyn,JsonSourceGenerator必须等待CustomGenerator输出代码后再运行,确保Json生成器能在同一轮构建中看到WeatherJsonContext并生成实现代码。
额外注意事项
- 移除.csproj中
<Compile Remove="$(CompilerGeneratedFilesOutputPath)/System.Text.Json.SourceGeneration/**/*.cs" />配置,避免误排除Json生成器的输出代码,导致编译失败。 - 确保项目引用.NET 6及以上版本的System.Text.Json包,因为Json源代码生成器是.NET 6引入的特性。
内容的提问来源于stack exchange,提问作者Viletung

