Happy Coding Json To C# Class (Mendeleev’s periodic table)

2023-07-04

快樂的開發系列之示範如何使用 C# 處理 Json 資料,同時說明 html template 的輸出技巧。

logo

說明

Program.cs 是本次示範的程式靈魂,主要從 GitHub 讀取元素週期表的 Json 資料。

接著透過 Json.NET 的 DeserializeObject 轉換為 C# 類別。

要進行這個動作必須先定義 C# Class,藉由選擇性貼上,將 Json 的 Schema 轉換成 C# Class,其中命名 Convention 的部分逐一調整,並且將 RootElement 換上更為適合的名稱 PeriodicTable

轉換成 C# 物件之後,處理就是行雲流水。目標是產出組合的字串,顯示化學元素的名稱以及化學模型,因為要重複組合字串,使用 StringBuilder 來處理。

StringBuilder 的好處包括:
效能優化:當需要連接大量字串時,使用 StringBuilder 比使用 + 或 += 連接運算符效能更好。這是因為 StringBuilder 在內部使用可調整大小的緩衝區,以減少記憶體重複配置和字串複製的次數。
避免產生多個臨時字串物件:在使用 + 或 += 連接運算符時,每次連接都會產生一個新的字串物件。對於大量連接操作,這可能會產生許多臨時的、不再使用的字串物件,佔用額外的記憶體並增加垃圾回收的負擔。而 StringBuilder 在內部緩衝區的基礎上進行操作,避免了這種額外的記憶體配置和回收。

為了增加可讀性,使用 template.html 的方式,當作 html 的 layout,並使用 使用 File.ReadAllText("template.html") 讀取;要插入的字串則結合 interpolated string 以及 verbatim string 來排版字串內容。

最後結果使用 File.WriteAllText 寫入至檔案,並搭配 Environment.GetFolderPath(Environment.SpecialFolder.Desktop) 指定是在執行環境的桌面位置 😁

Program.cs

using Newtonsoft.Json;
using System;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

namespace JsonParsingExample
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string url = "https://raw.githubusercontent.com/Bowserinator/Periodic-Table-JSON/master/PeriodicTableJSON.json";

            using (HttpClient client = new HttpClient())
            {
                try
                {
                    string json = await client.GetStringAsync(url);
                    string desktop = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
                    string filePath = Path.Combine(desktop, "element.html");
                    string template = File.ReadAllText("template.html");

                    PeriodicTable periodicTable = JsonConvert.DeserializeObject<PeriodicTable>(json);

                    StringBuilder dataBuilder = new StringBuilder();

                    foreach (Element element in periodicTable.Elements)
                    {
                        string elementHtml = $@"
            <div class='col-md-3 my-3'>
                <ul class='list-group'>
                    <li class='list-group-item'>
                        {element.Symbol}
                        <img class='img-fluid' src='{element.Bohr_model_image}'/>
                    </li>
                </ul>
            </div>";
                        dataBuilder.Append(elementHtml);
                    }

                    File.WriteAllText(filePath, template.Replace("{{content_block}}", dataBuilder.ToString()));
                    Console.WriteLine("資料已成功寫入檔案。");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Error: {ex.Message}");
                }
            }

            Console.ReadLine();
        }
    }
}

Element.cs

namespace JsonParsingExample
{
    public class PeriodicTable
    {
        public Element[] Elements { get; set; }
    }

    public class Element
    {
        public string Name { get; set; }
        public string Appearance { get; set; }
        public float Atomic_mass { get; set; }
        public float? Boil { get; set; }
        public string Category { get; set; }
        public float? Density { get; set; }
        public string Discovered_by { get; set; }
        public float? Melt { get; set; }
        public float? Molar_heat { get; set; }
        public string Named_by { get; set; }
        public int Number { get; set; }
        public int Period { get; set; }
        public int Group { get; set; }
        public string Phase { get; set; }
        public string Source { get; set; }
        public string Bohr_model_image { get; set; }
        public string Bohr_model_3d { get; set; }
        public string Spectral_img { get; set; }
        public string Summary { get; set; }
        public string Symbol { get; set; }
        public int Xpos { get; set; }
        public int Ypos { get; set; }
        public int Wxpos { get; set; }
        public int Wypos { get; set; }
        public int[] Shells { get; set; }
        public string Electron_configuration { get; set; }
        public string Electron_configuration_semantic { get; set; }
        public float? Electron_affinity { get; set; }
        public float? Electronegativity_pauling { get; set; }
        public float?[] Ionization_energies { get; set; }
        public string Cpkhex { get; set; }
        public Image Image { get; set; }
        public string Block { get; set; }
    }

    public class Image
    {
        public string Title { get; set; }
        public string Url { get; set; }
        public string Attribution { get; set; }
    }

}

template.html

<!DOCTYPE html>
<html>
<head>
    <title>Bootstrap Layout</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css">
</head>
<body>
    <div class="container">
        <h1>Elements</h1>
        <div class="row">
            {{content_block}}

        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>