目錄
前言
實作原理
解析 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<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: https://github.com/feiyun0112/MyIO.ParseJsonAsClass.SourceGenerator