首页 > 其他分享 >学习Source Generators之从swagger中生成类

学习Source Generators之从swagger中生成类

时间:2024-04-02 09:55:24浏览次数:21  
标签:item JObject value Source Generators var using swagger

前面学习了一些Source Generators的基础只是,接下来就来实践一下,用这个来生成我们所需要的代码。
本文将通过读取swagger.json的内容,解析并生成对应的请求响应类的代码。

创建项目

首先还是先创建两个项目,一个控制台程序,一个类库。
image.png

添加swagger文件

在控制台程序中添加Files目录,并把swagger文件放进去。别忘了还需要添加AdditionalFiles。

<ItemGroup>
  <AdditionalFiles Include="Files\swagger.json" />
</ItemGroup>

image.png

实现ClassFromSwaggerGenerator

安装依赖

由于我们需要解析swagger,所以需要安装一下JSON相关的包。这里我们安装了Newtonsoft.Json。
需要注意的是,依赖第三方包的时候需要在项目文件添加下面内容:

<PropertyGroup>
  <GetTargetPathDependsOn>$(GetTargetPathDependsOn);GetDependencyTargetPaths</GetTargetPathDependsOn>
</PropertyGroup>
<Target Name="GetDependencyTargetPaths" AfterTargets="ResolvePackageDependenciesForBuild">
  <ItemGroup>
    <TargetPathWithTargetPlatformMoniker Include="@(ResolvedCompileFileDefinitions)" IncludeRuntimeDependency="false" />
  </ItemGroup>
</Target>

否则编译时会出现FileNotFound的异常。

构建管道

这里我们通过AdditionalTextsProvider筛选以及过滤我们的swagger文件。

var pipeline = context.AdditionalTextsProvider.Select(static (text, cancellationToken) =>
  {
      if (!text.Path.EndsWith("swagger.json", StringComparison.OrdinalIgnoreCase))
      {
          return default;
      }

      return JObject.Parse(text.GetText(cancellationToken)!.ToString());
  })
    .Where((pair) => pair is not null);

实现生成代码逻辑

接下来我们就解析Swagger中的内容,并且动态拼接代码内容。主要代码部分如下:

context.RegisterSourceOutput(pipeline, static (context, swagger) =>
 {

     List<(string name, string sourceString)> sources = new List<(string name, string sourceString)>();


     #region 生成实体
     var schemas = (JObject)swagger["components"]!["schemas"]!;
     foreach (JProperty item in schemas.Properties())
     {
         if (item != null)
         {
             sources.Add((HandleClassName(item.Name), $@"#nullable enable
using System;
using System.Collections.Generic;

namespace SwaggerEntities;
public {ClassOrEnum((JObject)item.Value)} {HandleClassName(item.Name)} 
{{
    {BuildProperty((JObject)item.Value)}
}}
"));
         }
     }
     foreach (var (name, sourceString) in sources)
     {
         var sourceText = SourceText.From(sourceString, Encoding.UTF8);

         context.AddSource($"{name}.g.cs", sourceText);
     }
     #endregion
     });

完整的代码如下:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Text;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;

namespace GenerateClassFromSwagger.Analysis
{
    [Generator]
    public class ClassFromSwaggerGenerator : IIncrementalGenerator
    {
        public void Initialize(IncrementalGeneratorInitializationContext context)
        {
            var pipeline = context.AdditionalTextsProvider.Select(static (text, cancellationToken) =>
            {
                if (!text.Path.EndsWith("swagger.json", StringComparison.OrdinalIgnoreCase))
                {
                    return default;
                }

                return JObject.Parse(text.GetText(cancellationToken)!.ToString());
            })
            .Where((pair) => pair is not null);

            context.RegisterSourceOutput(pipeline, static (context, swagger) =>
            {

                List<(string name, string sourceString)> sources = new List<(string name, string sourceString)>();


                #region 生成实体
                var schemas = (JObject)swagger["components"]!["schemas"]!;
                foreach (JProperty item in schemas.Properties())
                {
                    if (item != null)
                    {
                        sources.Add((HandleClassName(item.Name), $@"#nullable enable
using System;
using System.Collections.Generic;

namespace SwaggerEntities;
public {ClassOrEnum((JObject)item.Value)} {HandleClassName(item.Name)} 
{{
    {BuildProperty((JObject)item.Value)}
}}
                "));
                    }
                }
                foreach (var (name, sourceString) in sources)
                {
                    var sourceText = SourceText.From(sourceString, Encoding.UTF8);

                    context.AddSource($"{name}.g.cs", sourceText);
                }
                #endregion
            });
        }

        static string HandleClassName(string name)
        {
            return name.Split('.').Last().Replace("<", "").Replace(">", "").Replace(",", "");
        }
        static string ClassOrEnum(JObject value)
        {
            return value.ContainsKey("enum") ? "enum" : "partial class";
        }


        static string BuildProperty(JObject value)
        {
            var sb = new StringBuilder();
            if (value.ContainsKey("properties"))
            {
                var propertys = (JObject)value["properties"]!;
                foreach (JProperty item in propertys!.Properties())
                {
                    sb.AppendLine($@"
    public {BuildProertyType((JObject)item.Value)} {ToUpperFirst(item.Name)}  {{ get; set; }}
");
                }
            }
            if (value.ContainsKey("enum"))
            {
                foreach (var item in JsonConvert.DeserializeObject<List<int>>(value["enum"]!.ToString())!)
                {
                    sb.Append($@"
    _{item},
");
                }
                sb.Remove(sb.Length - 1, 1);
            }
            return sb.ToString();
        }

        static string BuildProertyType(JObject value)
        {
            ;
            var type = GetType(value);
            var nullable = value.ContainsKey("nullable") ? value["nullable"]!.Value<bool?>() switch
            {
                true => "?",
                false => "",
                _ => ""
            } : "";
            return type + nullable;
        }

        static string GetType(JObject value)
        {
            return value.ContainsKey("type") ? value["type"]!.Value<string>() switch
            {
                "string" => "string",
                "boolean" => "bool",
                "number" => value["format"]!.Value<string>() == "float" ? "float" : "double",
                "integer" => value["format"]!.Value<string>() == "int32" ? "int" : "long",
                "array" => ((JObject)value["items"]!).ContainsKey("items") ?
                $"List<{HandleClassName(value["items"]!["$ref"]!.Value<string>()!)}>"
                : $"List<{GetType((JObject)value["items"]!)}>",
                "object" => value.ContainsKey("additionalProperties") ? $"Dictionary<string, {GetType((JObject)value["additionalProperties"]!)}>" : "object",
                _ => "object"
            } : value.ContainsKey("$ref") ? HandleClassName(value["$ref"]!.Value<string>()!) : "object";
        }

        static unsafe string ToUpperFirst(string str)
        {
            if (str == null) return null;
            string ret = string.Copy(str);
            fixed (char* ptr = ret)
                *ptr = char.ToUpper(*ptr);
            return ret;
        }
    }
}

详细的处理过程大家可以仔细看看代码,这里就不一一解释了。

启动编译

接下来编译控制台程序。编译成功后可以看到生成了很多cs的代码。若是看不见,可以重启VS。
image.png
点开一个文件,可以看到内容,并且在上方提示自动生成,无法编辑。
image.png
到这我们就完成了通过swagger来生成我们的请求和响应类的功能。

结语

本文章应用SourceGenerator,在编译时读取swagger.json的内容并解析,成功生成了我们API的请求和响应类的代码。
我们可以发现,代码生成没有问题,无法移动或者编辑生成的代码。
下一篇文章我们就来学习下如何输出SourceGenerator生成的代码文件到我们的文件目录。

本文代码仓库地址https://github.com/fanslead/Learn-SourceGenerator

标签:item,JObject,value,Source,Generators,var,using,swagger
From: https://www.cnblogs.com/fanshaoO/p/18109928

相关文章

  • 学习Source Generators之IncrementalValueProvider
    前面我们使用了IIncrementalGenerator来生成代码,接下来我们来详细了解下IIncrementalGenerator的核心部分IncrementalValueProvider。介绍IncrementalValueProvider是基于管道的模式,将我们需要的数据进行处理转换后传递给SourceOutput。目前官方提供可用的Providers有如下几种:......
  • sourceinsight工具
    1、调出ProjectWindow框这个框默认没有,从View-Panels-ProjectWindow2、选中高亮Options->FileTypeOptions(Alt+Y)->Hightlightreferencestoselectedsymbcol......
  • Ask HN: Recommended resources to learn the Linux kernel and OS theory?
     https://news.ycombinator.com/item?id=20809666 SorecentlyIdidacoupleofminorpatchesontheFreeBSDandNetBSDkernelsandplayedwithsomelinuxkernel.Itwasthefirsttimeinaafewyears,I'vebeenexcitedaboutprogramming.Unfortunat......
  • Swagger 文档工具 设计、构建、文档化和使用您的 RESTful API
    SwaggerSwagger是一个功能强大的开源框架,支持大量工具生态系统,帮助您设计、构建、文档化和使用您的RESTfulAPI。使用SpringBoot您可以从swagger-springboot获取完整的项目演示。springboot-blog中文版文件结构可能如下所示:.|____main||____java|||____com|......
  • flutter加载网络图片错误EXCEPTION CAUGHT BY IMAGE RESOURCE SERVICE The following
    在flutter里使用image.network加载网络图片遇到错误══╡EXCEPTIONCAUGHTBYIMAGERESOURCESERVICE╞════════════════════════════════════════════════════ThefollowingSocketExceptionwasthrownresolvingani......
  • 学习Source Generators之IIncrementalGenerator
    前面我们用ISourceGenerator来实现代码生成。但是在官方的介绍中有这么一个警告:Warning:SourcegeneratorsimplementingISourceGeneratorhavebeendeprecatedinfavorofincrementalgenerators.意思是实现ISourceGenerator的源生成器已被弃用,取而代之的是增量生成器。......
  • 学习Source Generators之HelloWorld
    介绍源生成器是C#开发人员可以编写的一种新组件,允许执行两个主要操作:检索表示正在编译的所有用户代码的编译对象。可以检查此对象,并且可以编写适用于正在编译的代码的语法和语义模型的代码,就像现在使用分析器一样。生成可在编译过程中添加到编译对象的C#源文件。也就是......
  • Scala第十二章节(Source读取数据的功能、写入数据的功能以及学员成绩表案例)
    章节目标掌握Source读取数据的功能掌握写入数据的功能掌握学员成绩表案例1.读取数据在Scala语言的Source单例对象中中,提供了一些非常便捷的方法,从而使开发者可以快速的从指定数据源(文本文件,URL地址等)中获取数据,在使用Source单例对象之前,需要先导包,即......
  • 2024-03-26 16:26:50.745 [main] INFO c.a.d.s.b.a.DruidDataSourceAutoConfigure -
    2024-03-2616:42:38.759[main]INFOc.a.d.s.b.a.DruidDataSourceAutoConfigure-InitDruidDataSource2024-03-2616:42:43.114[main]INFOcom.alibaba.druid.pool.DruidDataSource-{dataSource-1}inited2024-03-2616:42:47.348[main]INFOcom.alibaba.druid.po......
  • SpringBoot3项目使用Knife4j时访问doc.html出现Knife4j文档请求异常且开发者工具网络
    1.在各个pom.xml中替换Knife4j的依赖版本,升级为4.0以上,如果找不到依赖可以在Maven配置中多添加几个镜像,或者使用汉化插件重启IDEA;<dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId......