目录
前言
实现原理
解析 JSON 字符串
匹配 C# 类型
生成 C# 类代码
Source Generator
使用
总结
前言
有时候,我们需要将通过 WebAPI 接收 JSON 字符串转换成 C# 代码。Visual Studio 提供了一个功能菜单可以轻松实现:
执行完成后,它会将生成的代码放在打开的的代码窗口中。
但是,如果有多个 JSON 字符串需要转换,这个过程非常繁琐,而且容易出错。
本文将介绍如何使用 Source Generator 将 JSON 字符串转换成 C# 类。
实现原理 解析 JSON 字符串首先,我们需要解析 JSON 字符串,分析它的结构,再对应到 C# 类。这里,我们使用 System.Text.Json 库。
通过JsonDocument.Parse
方法解析 JSON 字符串,它将返回一个JsonDocument
对象:
using var jsonDocument = JsonDocument.Parse(json);
下图很好的说明了JsonDocument
的结构:
-
一个
JsonDocument
由多个JsonElement
和JsonProperty
组成 -
一个
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 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 将完整流程实现。首先,我们定义了一个 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
的类,拿到namesapce
、classname
和 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
2、在项目中添加一个 JSON 文件
{
"code": 200,
"msg": "ok",
"obj":{"a":1,"subObj":{"a":1}},
"data": [
"1","2"
],
"array": [
{"a":1.0},
{"a":null}
]
}
3、在项目中添加一个 C# 文件
using MyIO;
namespace ConsoleApp1
{
[ParseJsonAsClass("sample.txt")]
internal partial class Class1
{
}
}
sample.txt
是上一步中添加的 JSON 文件的名称。
4、编译项目
相关源代码已上传到 GitHub: https://github.com/feiyun0112/MyIO.ParseJsonAsClass.SourceGenerator