前言
有时候,我们需要将通过 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<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();
}
使用
在项目中安装 NuGet 包
dotnet add package MyIO.ParseJsonAsClass.SourceGenerator
在项目中添加一个 JSON 文件
{"code": 200,"msg": "ok","obj":{"a":1,"subObj":{"a":1}},"data": ["1","2"],"array": [{"a":1.0},{"a":null}]
}
在项目中添加一个 C# 文件
using MyIO;
namespace ConsoleApp1
{[ParseJsonAsClass("sample.txt")]internal partial class Class1{ }
}
sample.txt
是上一步中添加的 JSON 文件的名称。
编译项目
总结
相关源代码已上传到 GitHub: https://github.com/feiyun0112/MyIO.ParseJsonAsClass.SourceGenerator,点击“阅读原文”可直达,欢迎 Star。
添加微信号【MyIO666】,邀你加入技术交流群