前面学习了一些Source Generators的基础只是,接下来就来实践一下,用这个来生成我们所需要的代码。

本文将通过读取swagger.json的内容,解析并生成对应的请求响应类的代码。

创建项目

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

添加swagger文件

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

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

实现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。



点开一个文件,可以看到内容,并且在上方提示自动生成,无法编辑。



到这我们就完成了通过swagger来生成我们的请求和响应类的功能。

结语

本文章应用SourceGenerator,在编译时读取swagger.json的内容并解析,成功生成了我们API的请求和响应类的代码。

我们可以发现,代码生成没有问题,无法移动或者编辑生成的代码。

下一篇文章我们就来学习下如何输出SourceGenerator生成的代码文件到我们的文件目录。

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

学习Source Generators之从swagger中生成类的更多相关文章

  1. Typescript 学习笔记四:回忆ES5 中的类

    中文网:https://www.tslang.cn/ 官网:http://www.typescriptlang.org/ 目录: Typescript 学习笔记一:介绍.安装.编译 Typescrip ...

  2. 《CoffeeScript应用开发》学习:第五章 CoffeeScript中的类

    在CoffeeScript中定义类 在CoffeeScript中,使用class定义类,使用关键字new实例化对象. 给类绑定方法 class Airplane takeOff: -> cons ...

  3. 使用 MVVM Toolkit Source Generators

    关于 MVVM Toolkit 最近 .NET Community Toolkit 发布了 8.0.0 preview1,它包含了从 Windows Community Toolkit 迁移过来的以下 ...

  4. .NET初探源代码生成(Source Generators)

    前言 Source Generators顾名思义代码生成器,可进行创建编译时代码,也就是所谓的编译时元编程,这可让一些运行时映射的代码改为编译时,同样也加快了速度,我们可避免那种昂贵的开销,这是有价值 ...

  5. Spring Boot中使用Swagger CodeGen生成REST client

    文章目录 什么是Open API规范定义文件呢? 生成Rest Client 在Spring Boot中使用 API Client 配置 使用Maven plugin 在线生成API Spring B ...

  6. 【学习笔记】Java中生成对象的5中方法

    概述:本文介绍以下java五种创建对象的方式: 1.用new语句创建对象,这是最常用的创建对象的方式. 2.使用Class类的newInstance方法 3.运用反射手段,调用java.lang.re ...

  7. golang学习笔记7 使用beego swagger 实现API自动化文档

    golang学习笔记7 使用beego swagger 实现API自动化文档 API 自动化文档 - beego: 简约 & 强大并存的 Go 应用框架https://beego.me/doc ...

  8. ASP.NET Core 2 学习笔记(十三)Swagger

    Swagger也算是行之有年的API文件生成器,只要在API上使用C#的<summary />文件注解标签,就可以产生精美的线上文件,并且对RESTful API有良好的支持.不仅支持生成 ...

  9. Swagger自动生成接口文档

    <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://mave ...

  10. Python中生成随机数

    目录 1. random模块 1.1 设置随机种子 1.2 random模块中的方法 1.3 使用:生成整形随机数 1.3 使用:生成序列随机数 1.4 使用:生成随机实值分布 2. numpy.ra ...

随机推荐

  1. Direct2D 画刷篇

    微软文档:Brushes overview 本篇通过官方文档学习,整理出来的demo,初始样本请先创建一个普通的desktop app. // Test_Direct2D_Brush.cpp : De ...

  2. Redis服务端事件处理流程分析

    一.事件处理 1.1 什么是事件 Redis 为什么运行得比较快? 原因之一就是它的服务端处理程序用了事件驱动的处理方式. 那什么叫事件处理?就是把处理程序当成一个一个的事件处理.比如我前面文章:服务 ...

  3. 【Android 逆向】apk反编译后重打包

    1. 执行 apktool b smali_dir smali_dir 为反编译出来的数据目录 执行后可能会报错 I: Building resources... W: /root/Desktop/t ...

  4. Python嵌套绘图并为条形图添加自定义标注

    论文绘图时经常需要多图嵌套,正好最近绘图用到了,记录一下使用Python实现多图嵌套的过程. 首先,实现 Seaborn 分别绘制折线图和柱状图. '''绘制折线图''' import seaborn ...

  5. Rock Pi开发笔记(三):Rock Pi 4B plus(基于瑞星微RK3399)板子硬件资源介绍

    前言   上一篇,概览了整个的rock pi大致系列,我们开始定位为RK3399做评估,入手RK3399,对基本的外设进行解说.   板载外设      USB3.0 × 2 USB2.0 × 2 千 ...

  6. 01、SECS的基本概念

    最近做的项目跟半导体设备相关,需要学习SECS相关的内容,把自己的学习记录分享出来,如有不足甚至错误的地方,请不吝赐教,十分感谢! 文章内容基本都是SECS协议的内容和参考的资料,只不过是加了自己的理 ...

  7. 【Azure Function App】Nodejs Function遇见WorkerProcessExitException : node exited with code -1073740791 (0xC0000409) 错误

    问题描述 Nodejs Function,使用Blob Trigger用于处理上传到Storage Blob的文件,但是最近发现偶发报错:Exception while executing funct ...

  8. 5、zookeeper应用场景-配置中心原理

    配置中心 使用 zookeeper的特性watcher监听器 工作中有这样的一个场景:数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存 若数据库的用户名和密码改变时候 ...

  9. Codeforces Round 924 (Div. 2)B. Equalize(思维+双指针)

    目录 题面 链接 题意 题解 代码 题面 链接 B. Equalize 题意 给一个数组\(a\),然后让你给这个数组加上一个排列,求出现最多的次数 题解 赛时没过不应该. 最开始很容易想到要去重,因 ...

  10. XAF Blazor FilterPanel 布局样式

    从上一篇关于ListView布局样式的文章中,我们知道XAFBlazor是移动优先的,如果想在PC端有更好的用户体验,我们需要对布局样式进行修改.这篇介绍在之前文章中提到的FilterPanel,它的 ...