前言
有時候,我們需要將通過 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】,邀你加入技術交流群