十倍程序员 | 使用 Source Generator 将 JSON 转换成 C# 类

前言

有时候,我们需要将通过 WebAPI 接收 JSON 字符串转换成 C# 代码。Visual Studio 提供了一个功能菜单可以轻松实现:

273a56b09fed9f3c0913d4e5796b1bb4.png

执行完成后,它会将生成的代码放在打开的的代码窗口中。

但是,如果有多个 JSON 字符串需要转换,这个过程非常繁琐,而且容易出错。

本文将介绍如何使用 Source Generator 将 JSON 字符串转换成 C# 类。

实现原理

解析 JSON 字符串

首先,我们需要解析 JSON 字符串,分析它的结构,再对应到 C# 类。这里,我们使用 System.Text.Json 库。

通过JsonDocument.Parse方法解析 JSON 字符串,它将返回一个JsonDocument对象:

using var jsonDocument = JsonDocument.Parse(json);

下图很好的说明了JsonDocument的结构:

93d361b51e745dac715661a8007e062f.png

  • 一个JsonDocument由多个JsonElementJsonProperty组成

  • 一个JsonElement包含多个JsonProperty

  • 一个JsonProperty的值也是一个JsonElement

通过递归遍历,我们可以解析出 JSON 字符串的结构。

匹配 C# 类型

接下来,我们需要将解析出的 JSON 字符串结构,匹配成 C# 类型。这里,我们使用如下代码来存储类和属性信息:

public class ParsedType
{ //名称public string Name { get; private set; }//类型public TypeEnum Type { get; private set; }//针对 Array 的类型public ParsedType InternalType { get; private set; }//属性列表public IList<PropertyInfo> Properties { get; internal set; }//是否是顶级类,用于区分嵌套子类public bool IsRoot { get; internal set; }
}public class PropertyInfo
{public string Name { get; private set; }public string JsonName { get; private set; }public ParsedType Type { get; private set; }
}

生成 C# 类代码

匹配出了 C# 类型,生成 C# 类代码就非常容易了。这里,我们使用如下代码:

WriteFileStart(sw,name_space,class_name);foreach (var type in types)
{WriteClass(sw, type);
}WriteFileEnd(sw);

types是上一步解析出的 ParsedType 集合。

Source Generator

现在,我们需要使用 Source Generator 将完整流程实现。首先,我们定义了一个 Attribute:

const string attributeText = @"using System;namespace MyIO
{[AttributeUsage(AttributeTargets.Class)]public sealed class ParseJsonAsClassAttribute : Attribute{public ParseJsonAsClassAttribute(string fileName){FileName = fileName;}public string FileName { get; set; }}
}
";context.AddSource("MyIO.ParseJsonAsClassAttribute.g", SourceText.From(attributeText, System.Text.Encoding.UTF8));

然后,我们遍历项目中所有声明了ParseJsonAsClassAttribute的类,拿到namesapceclassname和 JSON 字符串,生成 C# 类代码,然后写到项目中:

foreach (var memberSyntax in memberSyntaxes)
{if (memberSyntax is ClassDeclarationSyntax classDeclarationSyntax){var name_space = GetNamespace(classDeclarationSyntax);var class_name = classDeclarationSyntax.Identifier.ValueText;string json = GetJson(classDeclarationSyntax);if (json == null){continue;}var sourceText = GenerateSource(name_space, class_name, json);if (sourceText != null){this.context.AddSource("MyIO.ParseJsonAsClass." + classDeclarationSyntax.Identifier.ValueText + ".g", sourceText);}}this.context.CancellationToken.ThrowIfCancellationRequested();
}

使用

  1. 在项目中安装 NuGet 包

dotnet add package MyIO.ParseJsonAsClass.SourceGenerator
  1. 在项目中添加一个 JSON 文件

{"code": 200,"msg": "ok","obj":{"a":1,"subObj":{"a":1}},"data": ["1","2"],"array": [{"a":1.0},{"a":null}]
}
  1. 在项目中添加一个 C# 文件

using MyIO;
namespace ConsoleApp1
{[ParseJsonAsClass("sample.txt")]internal partial class Class1{ }
}

sample.txt 是上一步中添加的 JSON 文件的名称。

  1. 编译项目

f74e2d0b6a15c339ab40585ec5920938.jpeg

总结

相关源代码已上传到 GitHub: https://github.com/feiyun0112/MyIO.ParseJsonAsClass.SourceGenerator,点击“阅读原文”可直达,欢迎 Star。

添加微信号【MyIO666】,邀你加入技术交流群

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282364.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Delphi对话框初始地址InitialDir

我的电脑&#xff1a;SaveDialog1.InitialDir : ::{20D04FE0-3AEA-1069-A2D8-08002B30309D};// My Computer {20D04FE0-3AEA-1069-A2D8-08002B30309D}// Network Neighborhood {208D2C60-3AEA-1069-A2D7-08002B30309D}// Recycled {645FF040-5081-101B-9F08-00AA002F954E} 另外…

[python] 解决pip install download速度过慢问题 更换豆瓣源

""" python建立pip.ini.py 2016年4月30日 03:35:11 codegay """import osini"""[global] index-url https://pypi.doubanio.com/simple/ [install] trusted-hostpypi.doubanio.com """ pippathos.environ["…

Maven组件通过命令上传本地和私有仓库

安装本地包到本地仓库&#xff1a;mvn install:install-file -DgroupIdcom.xxx -DartifactIdmqtt-server-client -Dversion1.0.1 -Dpackagingjar -DfileE:\__vdt\MVVP\mqtt-server-client-1.0.1.jar -DpomFileE:\__vdt\MVVP\pom.xml安装本地包到私有仓库&#xff1a;mvn deploy…

Nginx -静态资源Web服务

一、静态资源类型 注&#xff1a;非服务器动态生成的文件 1、浏览器端渲染 HTML、css、js 2、图片 jpeg、gif、png 3、视频 flv、MPEG 4、文件 TXT、等任意下载文件二、静态资源服务配置1、配置语法-文件读取 syntax&#xff1a;sendfile on|off default&#xff1a;sendfi…

微软Microsoft Azure 机器学习工作室的案例之Image Classification using DenseNet

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;10分钟)Microsoft Azure Machine Learning Studio是微软强大的机器学习平台&#xff0c;在设计器中&#xff0c;微软内置了15个场景案例&#xff0c;但网上似乎没有对这15个案例深度刨析的分析资料&#xff0c;所以我…

java小基础之instanceof运算符

instanceof主要用来判断一个类是否实现了某个接口&#xff0c;或者判断一个实例对象是否属于一个类。 1. 判断一个对象是否属于一个类 boolean result p instanceof Student; 2. 对象类型强制转换前的判断 Person p new Student(); //判断对象p是否为Student类的实例 if(p in…

音乐分类

代码&#xff1a; 1 import numpy as np2 from scipy import fft3 from scipy.io import wavfile4 from sklearn.linear_model import LogisticRegression5 import random6 """7 使用logistic regression处理音乐数据&#xff0c;音乐数据训练样本的获得是使…

Problem C: 类的初体验(III)

Description 定义一个类Data&#xff0c;只有一个double类型的属性和如下4个方法&#xff1a; 1. 缺省构造函数&#xff0c;将属性初始化为0&#xff0c;并输出“Initialize a data 0”。 2. 带参构造函数&#xff0c;将属性初始化为指定参数&#xff0c;并输出“Initialize…

Nginx- 实现跨域访问

一、什么是跨域 跨域&#xff1a;由于浏览器的同源策略&#xff0c;即属于不同域的页面之间不能相互访问各自的页面内容。详细见下表&#xff1a; 注&#xff1a;同源策略&#xff0c;单说来就是同协议&#xff0c;同域名&#xff0c;同端口 URL说明是否允许通信http://www.a…

不管对不对,先把闹钟关了再说

小榆提前关闭早上闹钟&#xff0c;几乎工作日的早晨都是被这魔怔的铃声给拉扯醒&#xff0c;无论有多么不愿还是痛苦&#xff0c;可对这闹钟也无可奈何&#xff0c;就算一时果断掐掉接下来是另一回麻烦事。最后一天&#xff0c;已经顾不得多少&#xff0c;没什么令人惧怕的人或…

pycharm(windows)安装及其设置中文菜单

pycharm&#xff08;windows&#xff09;安装及其设置中文菜单 1.下载 在官网&#xff08;http://www.jetbrains.com/pycharm/download/#sectionwindows&#xff09;进行下载 或者到百度云进行下载 专业版&#xff1a;链接&#xff1a;http://pan.baidu.com/s/1bSSRds 密码&…

Tomcat定义虚拟主机案例

Tomcat定义虚拟主机案例 作者&#xff1a;尹正杰 版权声明&#xff1a;原创作品&#xff0c;谢绝转载&#xff01;否则将追究法律责任。 一.准备环境 1>.创建web程序的根目录 [rootyinzhengjie ~]# mkdir -pv /home/yinzhengjie/data/www/webapps/ROOT mkdir: created direc…

node服务成长之路

我们的系统也从第一代平台开始到现在第四代平台更换中&#xff0c;对这四代平台做一个简单的介绍&#xff1a; 第一代平台&#xff0c;主要是集中式&#xff0c;以快速上线为目的&#xff1b;第二代平台主要是分布式改造&#xff0c;缓解各服务压力&#xff1b;第三代平台主要做…

将域名绑定到ip上,并实现访问不同二级子域名对应不同目录

一、将域名绑定到ip上1、环境介绍&#xff1a;阿里云服务器ESC&#xff08;美国硅谷&#xff09; 2、购买域名 3、备案 注&#xff1a;由于我买的是美国地区服务器&#xff0c;所以不用备案&#xff0c;如果买的国内服务器&#xff0c;这里需要添加一个备案操作。 4、域名实名认…

ABP vNext微服务架构详细教程(补充篇)——单层模板(中)

框架搭建2聚合服务这里我们将聚合服务命名为Domain.Core和基础服务层一致&#xff0c;我们先通过命令创建单层模板项目Domain.Core&#xff0c;这里我们删除wwwroot、Data、Entities、Localization、ObjectMapping文件夹及其所有子文件&#xff0c;并删除package.json文件和Ser…

谈一谈synchronized关键词

1.使用 java中的每一个对象都可以作为synchronized的锁进行代码同步&#xff0c;常见的形式 同步代码块锁是synchronized括号内的对象普通成员方法上&#xff0c;锁是当前的对象&#xff0c;synchronized(this)静态方法上&#xff0c;锁是当前类的Class对象2. 原理 synchronize…

系统学习redis之二——redis集群搭建

redis单点部署&#xff1a; 安装命令&#xff1a; # cd /usr/local/ # wget http://download.redis.io/releases/redis-4.0.1.tar.gz #下载安装包 # yum -y install gcc psmisc #安装依赖包 # tar xf redis-4.0.1.tar.gz # cd /usr/lo…

业务技术协同线上化的研发管理实战

摘要&#xff1a;2017年1月13日举办的【云栖计算之旅】线下沙龙第4期研发管理专场&#xff0c;阿里巴巴B2B事业群产品专家代平为大家带来了题为业务技术协同线上化的研发管理实战的演讲。本文主要从管理产品研发的理念开始谈起&#xff0c;着重说明了云效指挥部的六大步骤&…

Linux中写脚本,同时去开启我们自己设定的多个服务(含定时脚本实现)

场景介绍&#xff1a; 在Linux中&#xff0c;我们通常开启服务需要使用systemctl start 服务名 命令&#xff0c;这样&#xff0c;如果开启一个服务还好&#xff0c;但是如果同时开启多个服务&#xff0c;难免会感到麻烦&#xff0c;这时&#xff0c;我们可以自定义一个脚本&a…

Android之利用EventBus进行数据传递

在项目中&#xff0c;不可避免的要在两个页面之间进行数据的传递&#xff0c;就算不传递&#xff0c;也需要进行刷新之类的&#xff0c;我们根据Google提供的库类方法&#xff0c;也是可以做的&#xff0c;主要有广播broadcastreceiver&#xff0c;startactivity方法或者是appl…