Проблема с использованием System.Text.Json вместе с генератором исходного кода

Рейтинг: 1Ответов: 1Опубликовано: 23.01.2023

Хотел из json файла нагенерировать C#-классов с помощью современного ISourceGenerator. Данные - массив блоков Minecraft.

Всё бы ничего, но использование JsonSerizlizer.Deserialize постоянно вызывает следующую ошибку, в следствии которой мой генератор крашится:

Генератору "BlocksGenerator" не удалось создать источник. Это не повлияет на выходные данные и ошибки компиляции, которые могут возникнуть в результате. Тип возникшего исключения: "FileNotFoundException", сообщение: "Не удалось загрузить файл или сборку "Microsoft.Bcl.AsyncInterfaces, Version=7.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51" либо одну из их зависимостей. Не удается найти указанный файл."

У меня есть простейшее решение, структура такая:

- Minever.Data
- Minever.Data.Core
|-- IBlock.cs
- Minever.Data.Generators
|-- BlocksGenerators.Block.cs
|-- BlocksGenerators.cs

GitHub проекта: https://github.com/iiKuzmychov/Minever.Data.

BlocksGenerators.Block.cs:

using System.Text.Json.Serialization;

namespace Minever.Data.Generators;

public partial class BlocksGenerator
{
    internal class Block
    {
        [JsonPropertyName("id")]
        public int Id { get; set; }

        [JsonPropertyName("name")]
        public string Name { get; set; } = default!;
    }
}

BlocksGenerator.cs:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Minever.Data.Core;
using Minever.Data.Generators.Utils;
using System.Diagnostics;
using System.Text;
using System.Text.Json;

namespace Minever.Data.Generators;

[Generator]
public partial class BlocksGenerator : ISourceGenerator
{
    public void Execute(GeneratorExecutionContext context)
    {
        var json   = @"[ {""id"":0, ""name"": ""air""} ]"; // json для теста
        var blocks = JsonSerializer.Deserialize<Block[]>(json)!; // в ЭТОЙ строке исключение

        var sourceBuilder = new StringBuilder();
        sourceBuilder.AppendLine($"using {nameof(Minever)}.{nameof(Data)}.{nameof(Core)};");
        sourceBuilder.AppendLine();
        sourceBuilder.AppendLine($"namespace {nameof(Minever)}.{nameof(Data)}.Blocks;");
        sourceBuilder.AppendLine();

        foreach (var block in blocks)
        {
            sourceBuilder.AppendLine(GenerateBlockSourceCode(block));
            sourceBuilder.AppendLine();
        }

        context.AddSource("Blocks.g.cs", SourceText.From(sourceBuilder.ToString(), Encoding.UTF8));
    }

    public void Initialize(GeneratorInitializationContext context)
    {
#if DEBUG
        if (!Debugger.IsAttached)
            Debugger.Launch();
#endif
    }

    private string GenerateBlockSourceCode(Block block) =>
        $@"public class {block.Name.ToPascalCase()} : {nameof(IBlock)}
{{
    public int {nameof(IBlock.Id)} {{ get; }} = {block.Id};
    public string {nameof(IBlock.Name)} {{ get; }} = {block.Name};
}}";
}

Minever.Data.Generators.sln:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <LangVersion>11.0</LangVersion>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
    <EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.4.0" />
    <PackageReference Include="System.Text.Json" Version="7.0.1" GeneratePathProperty="true" />
  </ItemGroup>

  <ItemGroup>
    <ProjectReference Include="..\Minever.Data.Core\Minever.Data.Core.csproj" />
  </ItemGroup>
  
  <PropertyGroup>
    <GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
  </PropertyGroup>

  <Target Name="GetDependencyTargetPaths">
    <ItemGroup>
      <TargetPathWithTargetPlatformMoniker Include="$(PKGSystem_Text_Json)\lib\netstandard2.0\System.Text.Json.dll" IncludeRuntimeDependency="false" />
    </ItemGroup>
  </Target>
  
</Project>

UPD:

Попробовал изменить платформу генератора на .NET 7. После этого он перестал запускаться, в принципе, и имеется следующая ошибка:

Экземпляр анализатора Minever.Data.Generators.BlocksGenerator невозможно создать из D:\IT Projects\Other\Minever.Data\src\Minever.Data.Generators\bin\Debug\net7.0\Minever.Data.Generators.dll : Не удалось загрузить файл или сборку "System.Runtime, Version=7.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" либо одну из их зависимостей. Не удается найти указанный файл."

Ответы

▲ 1Принят

Исходя из того что я вижу и пишу в комментариях к вопросу, здесь 2 основных пути решения:

  1. Мигрировать библиотеку на .NET Core 3/5/6/7 (.NET Core 3.1 может быть несовместим с ISourceGenerator, поэтому рекомендую .NET 6 и новее)
  2. Использовать совместимый со старыми API JSON.NET (Newtonsoft.Json) вместо System.Text.Json

Все остальные варианты, такие как выкачивание более старых версий System.Text.Json из NuGet выглядят костыльными и не стоят вашего внимания, на мой взгляд (Include="System.Text.Json" Version="7.0.1" - взять версию постарше).

Выбор решения за вами и зависит от требований к проекту.