Program

[C# Program] Json 계층 구조 출력 프로그램

nowkoes 2024. 7. 12. 00:00

개요

 C#에서 JSON 파일을 파싱할 때 보통 각 계층을 클래스로 만들어 HAS-A 관계로 포함시키는 것이다. 개발자 입장에서 JSON 파일을 열어본 후, 해당 구조를 일일이 파악해서 클래스로 만드는 것이 여간 귀찮은 게 아니다. 그래서 JSON 파일을 넣으면 자동으로 계층을 보기 좋게 출력 해주는 프로그램을 만들어 봤다. 추후엔 이 로직을 바탕으로 자동으로 클래스를 생성해주는 프로그램을 만들 수 있지 않을까 라는 생각도 해본다.


본문

사용법 (exe 파일 활용)

JsonParser.msi
0.96MB
setup.exe
0.55MB

 

 

 JsonParser.msi 파일을 다운받은 뒤, 해당 프로그램을 설치하고 JsonParse.exe 파일을 실행시키면 다음과 같은 화면이 뜰 것이다. 여기서 Select JSON File을 선택하자.

 

 

 다음과 같이 파일 탐색기가 뜰 것인데, 파싱을 하고 싶은 json 파일을 선택하자.

 

 

 그러면 다음과 같이 깊이 별로 어떤 정보가 있는지, 그리고 계층도가 어떻게 이루어져있는지 출력해준다.


사용법 (코드 활용)

더보기
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using System.Windows.Forms;

namespace JsonFileParser
{
    public partial class Form1 : Form
    {
        class JsonHierarchyAnalyzer
        {
            public static void AnalyzeJsonHierarchy(string jsonFilePath, TextBox outputTextBox)
            {
                string jsonContent = File.ReadAllText(jsonFilePath);
                using (JsonDocument jsonDocument = JsonDocument.Parse(jsonContent))
                {
                    Dictionary<int, HashSet<string>> hierarchy = new Dictionary<int, HashSet<string>>();
                    TreeNode rootNode = new TreeNode("root");
                    int maxDepth = 0;
                    AnalyzeElement(jsonDocument.RootElement, 0, hierarchy, rootNode, ref maxDepth);

                    List<int> emptyDepths = new List<int>();
                    foreach (var depth in hierarchy)
                    {
                        if (depth.Value.Count == 0)
                        {
                            emptyDepths.Add(depth.Key);
                        }
                    }
                    foreach (var emptyDepth in emptyDepths)
                    {
                        hierarchy.Remove(emptyDepth);
                    }

                    outputTextBox.AppendText($"Total Depth: {maxDepth}{Environment.NewLine}");
                    PrintHierarchyByDepth(hierarchy, outputTextBox);

                    outputTextBox.AppendText(new string('-', 50) + Environment.NewLine);

                    PrintHierarchyTree(rootNode, outputTextBox);
                }
            }

            private static void AnalyzeElement(JsonElement element, int currentDepth, Dictionary<int, HashSet<string>> hierarchy, TreeNode currentNode, ref int maxDepth)
            {
                if (!hierarchy.ContainsKey(currentDepth))
                {
                    hierarchy[currentDepth] = new HashSet<string>();
                }

                switch (element.ValueKind)
                {
                    case JsonValueKind.Object:
                        foreach (JsonProperty property in element.EnumerateObject())
                        {
                            hierarchy[currentDepth].Add(property.Name);
                            TreeNode childNode = currentNode.AddChild(property.Name);
                            AnalyzeElement(property.Value, currentDepth + 1, hierarchy, childNode, ref maxDepth);
                        }
                        break;

                    case JsonValueKind.Array:
                        foreach (JsonElement item in element.EnumerateArray())
                        {
                            AnalyzeElement(item, currentDepth, hierarchy, currentNode, ref maxDepth);
                        }
                        break;
                }

                if (currentDepth > maxDepth)
                {
                    maxDepth = currentDepth;
                }
            }

            private static void PrintHierarchyByDepth(Dictionary<int, HashSet<string>> hierarchy, TextBox outputTextBox)
            {
                foreach (var depth in hierarchy)
                {
                    outputTextBox.AppendText($"Depth {depth.Key}:{Environment.NewLine}");
                    foreach (var key in depth.Value)
                    {
                        outputTextBox.AppendText($"  {key}{Environment.NewLine}");
                    }
                }
            }

            private static void PrintHierarchyTree(TreeNode node, TextBox outputTextBox, string indent = "", bool isLast = true)
            {
                outputTextBox.AppendText(indent + (isLast ? "└── " : "├── ") + node.Name + Environment.NewLine);
                indent += isLast ? "    " : "│   ";

                for (int i = 0; i < node.Children.Count; i++)
                {
                    PrintHierarchyTree(node.Children[i], outputTextBox, indent, i == node.Children.Count - 1);
                }
            }
        }

        class TreeNode
        {
            public string Name { get; }
            public List<TreeNode> Children { get; }

            public TreeNode(string name)
            {
                Name = name;
                Children = new List<TreeNode>();
            }

            public TreeNode AddChild(string name)
            {
                foreach (var child in Children)
                {
                    if (child.Name == name)
                    {
                        return child;
                    }
                }

                var newChild = new TreeNode(name);
                Children.Add(newChild);
                return newChild;
            }
        }

        private TextBox outputTextBox;
        private Button selectFileButton;

        public Form1()
        {
            InitializeComponent();
            InitializeCustomComponents();
        }

        private void InitializeCustomComponents()
        {
            this.outputTextBox = new TextBox
            {
                Multiline = true,
                ScrollBars = ScrollBars.Vertical,
                Dock = DockStyle.Fill,
                WordWrap = true,  // 줄 바꿈 활성화
            };

            this.selectFileButton = new Button
            {
                Text = "Select JSON File",
                Dock = DockStyle.Top
            };
            this.selectFileButton.Click += new EventHandler(this.SelectFileButton_Click);

            this.Controls.Add(this.outputTextBox);
            this.Controls.Add(this.selectFileButton);
        }

        private void SelectFileButton_Click(object sender, EventArgs e)
        {
            using (OpenFileDialog openFileDialog = new OpenFileDialog())
            {
                openFileDialog.Filter = "JSON files (*.json)|*.json|All files (*.*)|*.*";
                if (openFileDialog.ShowDialog() == DialogResult.OK)
                {
                    string jsonFilePath = openFileDialog.FileName;
                    outputTextBox.Clear();
                    JsonHierarchyAnalyzer.AnalyzeJsonHierarchy(jsonFilePath, outputTextBox);
                }
            }
        }
    }
}

 

 

 혹시 실행 파일이 찝찝하면 아래에 접은 글로 코드를 보고 자신의 입맛에 맞게 수정한 뒤 사용하면 좋을 것 같다. 필자의 경우 JSON 요소를 재귀적으로 순회하며 현재 깊이를 추적하고, 각 깊이에 있는 키를 hierarchy 딕셔너리에 저장하게 하였다. 그리고 해당 딕셔너리에 저장된 각 깊이와 해당 깊이의 키들을 출력하고, 트리 구조를 구축하게 로직을 구성하였다. 이후의 사용법은 위와 동일하니 JSON 파일의 구조를 파악할 때 참고하면 되겠다.

반응형