【C#】ツリー構造となるクラス群を作成するサンプルコード

# 概要
C#で、ツリー構造となるクラス群を作成するサンプルコードを記載しました。

# 説明
[こちら](#xml)のような構造のxmlファイルがあるとします。
また、`Node`という名前のクラスを用意して、このクラス一つがxmlデータの一つの層 (`Node`) を表すとします。
このxmlファイルの内容を読み込み、各`Node`クラスにxmlの内容を書き出し、xmlと同じツリー構造にするサンプルコードを記載しました。

# コード
### xml
第一階層から第四階層まであり、第四階層のみ`Text`要素という付加情報の要素がある構成です。
```xml:TreeNode.xml
<?xml version="1.0" encoding="Shift-JIS"?>
<Data>
    <Layer1 NodeID="01_01">最上層ノード
        <Layer2 NodeID="02_01">第二階層ノード①
            <Layer3 NodeID="03_01">第三階層ノード①
                <Layer4 NodeID="04_01">最下層ノード①
                    <Text TextID="A">テキスト①</Text>
                </Layer4>
                <Layer4 NodeID="04_02">最下層ノード②
                    <Text TextID="B">テキスト②</Text>
                    <Text TextID="C">テキスト③</Text>
                </Layer4>
            </Layer3>
            <Layer3 NodeID="03_02">第三階層ノード②
                <Layer4 NodeID="04_03">最下層ノード③
                    <Text TextID="D">テキスト④</Text>
                    <Text TextID="E">テキスト⑤</Text>
                </Layer4>
                <Layer4 NodeID="04_04">最下層ノード④
                    <Text TextID="F">テキスト⑥</Text>
                </Layer4>
            </Layer3>
        </Layer2>
        <Layer2 NodeID="02_02">第二階層ノード②
            <Layer3 NodeID="03_03">第三階層ノード③
                <Layer4 NodeID="04_05" >最下層ノード⑤
                    <Text TextID="G">テキスト⑦</Text>
                    <Text TextID="H">テキスト⑧</Text>
                    <Text TextID="I">テキスト⑨</Text>
                    <Text TextID="J">テキスト⑩</Text>
                </Layer4>
            </Layer3>
        </Layer2>
    </Layer1>
</Data>

```
### C#
ノードクラスです。
このクラス一つがxmlの一つの層を表しています。

```C#:Node.cs
    public class Node
    {
        /// <summary>
        /// 階層
        /// </summary>
        public string Layer { get; set; }

        /// <summary>
        /// ノードごとに一意になるID
        /// </summary>
        public string NodeID { get; set; }

        /// <summary>
        /// ノードの名称 (xmlのLayer要素のコンテンツ部分)
        /// </summary>
        public string Name { get; set; }
        
        /// <summary>
        /// 最下層ノードの付加情報
        /// </summary>
        public List<Text> Text { get; set; } = new List<Text>();
        
        /// <summary>
        /// 子ノード群
        /// </summary>
        public List<Node> Children { get; } = new List<Node>();

        /// <summary>
        /// 自身とすべての子ノードを列挙
        /// </summary>
        public IEnumerable<Node> SelfAndDescendants
        {
            get
            {
                yield return this;
                foreach (var child in Children)
                {
                    foreach (var descendant in child.SelfAndDescendants)
                    {
                        yield return descendant;
                    }
                }
            }
        }
    }

```

xmlの第四階層の子要素で定義されているText要素一つ分のクラスです。

```C#:Text.cs
    public class Text
    {
        /// <summary>
        /// 付加ごとに一意になるID
        /// </summary>
        public string TextID { get; set; }

        /// <summary>
        /// 付加情報の名称 (xmlのText要素のコンテンツ部分)
        /// </summary>
        public string Name { get; set; }
    }
```
xmlを読み込み、ツリー構造となるクラス群を作成するコードを記載したクラスです。
名前は仮に`SampleClass`としています。

```C#:SampleClass.cs
    Public class SampleClass
    {
        /// <summary>
        /// ツリーのルートノード (第一階層)
        /// </summary>
        public Node Root { get; set; }
    
        /// <summary>
        /// TreeNode.xmlを読み込みツリーを作成する
        /// </summary>
        public void LoadTreeNode()
        {
            var path = (TreeNode.xmlまでのパス);
            var xml = XElement.Load(path);
            Root = CreateNode(xml.Element("Layer1"));
        }

        /// <summary>
        /// ノードを作成する
        /// </summary>
        private Node CreateNode(XElement element)
        {
            var node = new Node()
            {
                NodeID = element.Attribute("NodeID")?.Value ?? "",
                Layer = element.Name.LocalName,
                Name = element.FirstNode.ToString().Replace("\n", "").Replace("\r", "").Trim()
            };
        
            foreach (var childElement in element.Elements())
            {
                if (childElement.Name.LocalName.Contains("Text"))
                {
                    var text = new Text(node)
                    {
                        TextID = childElement.Attribute("TextID")?.Value ?? "",
                        Name = childElement.FirstNode.ToString().Replace("\n", "").Replace("\r", "").Trim()
                    };

                    node.Text.Add(text);
                }
                else
                {
                    // 子ノードを読み込む
                    node.Children.Add(CreateNode(node, childElement));
                }
            }
                
            return node;
       }
    
    }
```

- 上記`SampleClass`の`LoadTreeNode()`を呼び出すと、xmlから全てのノードの情報を読み込んで各ノードクラスに格納していき、第一階層のノードが`Root`プロパティに入ります。
- 各`Node`クラスの`Children`プロパティを使用して、そのノードの子ノードをみることができます。
- `CreateNode()`メソッドで再起処理を使用してノードを作成していっています。
- 下記のように`Node`クラスの`SelfAndDescendants`プロパティを呼び出すと、そのノードと子ノードを全て列挙したコレクションが帰ってきます。

```C#:SampleClass.cs
    Public class SampleClass
    {
        /// <summary>
        /// ツリーのルートノード (第一階層)
        /// </summary>
        public Node Root { get; set; }

        private void DoSomething()
        {
            // 第一階層含む、全てのノードをコレクションで列挙
            var nodes = Root.SelfAndDescendants;

            // NodeIDが"02_02"の第二階層と子ノードをコレクションで列挙
            nodes = Root.Children.First(x => x.NodeID == "02_02").SelfAndDescendants;
        }
    }
```

# 終わりに
個人的に意外とツリー構造のクラス群を作成する機会が多かったので、自分用としてサンプルコードを記載してみました。