天天看點

十倍程式員 | 使用 Source Generator 将 JSON 轉換成 C# 類前言實作原理使用總結

目錄

前言

實作原理

解析 JSON 字元串

比對 C# 類型

生成 C# 類代碼

Source Generator

使用

總結

前言

有時候,我們需要将通過 WebAPI 接收 JSON 字元串轉換成 C# 代碼。Visual Studio 提供了一個功能菜單可以輕松實作:

十倍程式員 | 使用 Source Generator 将 JSON 轉換成 C# 類前言實作原理使用總結

執行完成後,它會将生成的代碼放在打開的的代碼視窗中。

但是,如果有多個 JSON 字元串需要轉換,這個過程非常繁瑣,而且容易出錯。

本文将介紹如何使用 Source Generator 将 JSON 字元串轉換成 C# 類。

實作原理

解析 JSON 字元串

首先,我們需要解析 JSON 字元串,分析它的結構,再對應到 C# 類。這裡,我們使用 System.Text.Json 庫。

通過

JsonDocument.Parse

方法解析 JSON 字元串,它将傳回一個

JsonDocument

對象:

using var jsonDocument = JsonDocument.Parse(json);
           

下圖很好的說明了

JsonDocument

的結構:

十倍程式員 | 使用 Source Generator 将 JSON 轉換成 C# 類前言實作原理使用總結
  • 一個

    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、編譯項目

十倍程式員 | 使用 Source Generator 将 JSON 轉換成 C# 類前言實作原理使用總結

總結

相關源代碼已上傳到 GitHub: https://github.com/feiyun0112/MyIO.ParseJsonAsClass.SourceGenerator