实现原理
▌解析 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<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的类,拿到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 获取相关源代码信息。
长按识别二维码