LoginSignup
0
0

More than 3 years have passed since last update.

XmlElementのラッパークラス

Last updated at Posted at 2020-02-17

ノード作成時のXmlDocument.CreateElementが面倒

XMLファイルをXmlDocumentで作成する場合、testAノードの下にtestBノードを作成したい時に、testA.AppendChild(testB)としたいのに、毎回XmlDocument.CreateElement(新しいノード)をする必要があるため、より直感的にAppendできように簡単なラッパークラスを考えてみました。

環境

IDE:VisualStudio2019
アプリケーション:コンソールアプリ
フレームワーク:.NET Core 3.1

XmlDocumentを持ちまわる

XmlElementをラップし、さらにXmlDocumentを持ちまわって、Append時にそのXmlDocumentでCreateElementさせます。

using System.Xml;

namespace TestProject
{
    /// <summary>
    /// XmlDocument拡張クラス
    /// </summary>
    public class XmlDocumentExtension : XmlDocument
    {
        /// <summary>
        /// ルート要素を作成
        /// ※既存の子ノードはすべて削除します
        /// </summary>
        /// <param name="el">要素</param>
        /// <param name="version">バージョン</param>
        /// <param name="encoding">エンコーディング</param>
        /// <param name="standalone">外部依存</param>
        /// <returns>XmlElementWrapper</returns>
        public XmlElementWrapper CreateRootOfElementWrapper(string el, string version = "1.0", string encoding = "utf-8", string standalone = null)
        {
            this.RemoveAll();
            this.AppendChild(this.CreateXmlDeclaration(version, encoding, standalone));
            var createEl = this.CreateElement(el);
            return new XmlElementWrapper(this.AppendChild(createEl));
        }
    }

    /// <summary>
    /// XmlElementラッパークラス
    /// </summary>
    public class XmlElementWrapper
    {
        /// <summary>
        /// XmlElement
        /// </summary>
        private XmlElement _el;

        /// <summary>
        /// XmlDocument
        /// </summary>
        private XmlDocument _doc;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="node">XmlNode</param>
        public XmlElementWrapper(XmlNode node)
        {
            // 自身のXmlElementを保持
            this._el = node as XmlElement;

            // XmlDocumentを保持
            this._doc = this._el.OwnerDocument;
        }

        /// <summary>
        /// 子要素を追加
        /// </summary>
        /// <param name="el">要素</param>
        /// <returns>追加した子要素</returns>
        public XmlElementWrapper AppendChild(string el)
        {
            var addEl = this._el.AppendChild(this._doc.CreateElement(el));
            return new XmlElementWrapper(addEl);
        }
    }
}

AppendChildで追加した子要素を新たにXmlElementWrapperでラップすることで、後続のエレメントでもAppendが楽になります。
※AppendChildの引数はstringですがXmlNode版もあるといいのかもしれません。

また、最初のrootエレメントを起点とすることにしたため、XmlDocumentの拡張クラスを用意しています。

このクラスの使用イメージは以下です。

using System;
using System.IO;
using TestProject.Extension;

namespace TestProject
{
    /// <summary>
    /// メインクラス
    /// </summary>
    public class Program
    {
        /// <summary>
        /// メインエントリ
        /// </summary>
        /// <param name="args">実行時引数</param>
        public static void Main(string[] args)
        {
            // XmlDocument拡張クラス
            var doc = new XmlDocumentExtension();

            // root要素作成
            var root = doc.CreateRootOfElementWrapper("root");

            // rootの直下にtestAを作成
            var testA = root.AppendChild("testA");

            // testAの下にtestBを作成
            var testB = testA.AppendChild("testB");

            // xml保存
            doc.Save(Path.Combine(Environment.CurrentDirectory, "test.xml"));
        }
    }
}

以下のxmlが作成されます。

<?xml version="1.0" encoding="utf-8"?>
<root>
  <testA>
    <testB />
  </testA>
</root>

必要なメソッドを実装していく

値設定/値取得や、XPath、属性設定等 必要に応じてメソッドを用意し、ラップしたXElementの各処理を実装します。

        /// <summary>
        /// 値を設定
        /// </summary>
        /// <param name="value">値</param>
        public void SetValue(string value)
        {
            this._el.InnerText = value;
        }

        /// <summary>
        /// 値を取得
        /// </summary>
        /// <returns>値</returns>
        public string GetValue()
        {
            return this._el.InnerText;
        }

        /// <summary>
        /// 要素検索
        /// </summary>
        /// <param name="xPath">XPath</param>
        /// <returns>XmlElementWrapper</returns>
        public XmlElementWrapper SelectSingleNode(string xPath)
        {
            var node = this._el.SelectSingleNode(xPath);
            return node == null ? null : new XmlElementWrapper(node);
        }

サンプル全文

using System.Xml;

namespace TestProject.Extension
{
    /// <summary>
    /// XmlDocument拡張クラス
    /// </summary>
    public class XmlDocumentExtension : XmlDocument
    {
        /// <summary>
        /// ルート要素を作成
        /// ※既存の子ノードはすべて削除します
        /// </summary>
        /// <param name="el">要素</param>
        /// <param name="version">バージョン</param>
        /// <param name="encoding">エンコーディング</param>
        /// <param name="standalone">外部依存</param>
        /// <returns>XmlElementWrapper</returns>
        public XmlElementWrapper CreateRootOfElementWrapper(string el, string version = "1.0", string encoding = "utf-8", string standalone = null)
        {
            this.RemoveAll();
            this.AppendChild(this.CreateXmlDeclaration(version, encoding, standalone));
            var createEl = this.CreateElement(el);
            return new XmlElementWrapper(this.AppendChild(createEl));
        }
    }

    /// <summary>
    /// XmlElementラッパークラス
    /// </summary>
    public class XmlElementWrapper
    {
        /// <summary>
        /// XmlElement
        /// </summary>
        private XmlElement _el;

        /// <summary>
        /// XmlDocument
        /// </summary>
        private XmlDocument _doc;

        /// <summary>
        /// タグ名
        /// </summary>
        public string Name => this._el.Name;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="node">XmlNode</param>
        public XmlElementWrapper(XmlNode node)
        {
            this._el = node as XmlElement;
            this._doc = this._el.OwnerDocument;
        }

        /// <summary>
        /// 子要素を追加
        /// </summary>
        /// <param name="el">要素</param>
        /// <returns>追加した子要素</returns>
        public XmlElementWrapper AppendChild(string el)
        {
            var addEl = this._el.AppendChild(this._doc.CreateElement(el));
            return new XmlElementWrapper(addEl);
        }

        /// <summary>
        /// 値を設定
        /// </summary>
        /// <param name="value">値</param>
        public void SetValue(string value)
        {
            this._el.InnerText = value;
        }

        /// <summary>
        /// 値を取得
        /// </summary>
        /// <returns>値</returns>
        public string GetValue()
        {
            return this._el.InnerText;
        }

        /// <summary>
        /// 属性を設定
        /// </summary>
        /// <param name="attrName">属性名</param>
        /// <param name="value">属性値</param>
        public void SetAttribute(string attrName, string attrValue)
        {
            this._el.SetAttribute(attrName, attrValue);
        }

        /// <summary>
        /// 属性を取得
        /// </summary>
        /// <param name="attrName">属性名</param>
        /// <returns>属性値</returns>
        public string GetAttribute(string attrName)
        {
            return this._el.GetAttribute(attrName);
        }

        /// <summary>
        /// 要素検索
        /// </summary>
        /// <param name="xPath">XPath</param>
        /// <returns>XmlElementWrapper</returns>
        public XmlElementWrapper SelectSingleNode(string xPath)
        {
            var node = this._el.SelectSingleNode(xPath);
            return node == null ? null : new XmlElementWrapper(node);
        }
    }
}
using System;
using System.IO;
using TestProject.Extension;

namespace TestProject
{
    /// <summary>
    /// メインクラス
    /// </summary>
    public class Program
    {
        /// <summary>
        /// メインエントリ
        /// </summary>
        /// <param name="args">実行時引数</param>
        public static void Main(string[] args)
        {
            try
            {
                // XmlDocument拡張クラス
                var doc = new XmlDocumentExtension();

                // root要素作成
                var root = doc.CreateRootOfElementWrapper("root");

                // rootの直下にtestAを作成
                var testA = root.AppendChild("testA");

                // testAの下にtestBを作成
                var testB = testA.AppendChild("testB");

                // testBの下に同じタグを追加
                var testTag1 = testB.AppendChild("testTag");
                var testTag2 = testB.AppendChild("testTag");

                // 値設定
                var value1 = "valueTag1";
                var value2 = "valueTag2";
                testTag1.SetValue(value1);
                testTag2.SetValue(value2);

                // 属性設定
                testTag1.SetAttribute("testAttr", "testAttrValue");

                // XPath(あえてrootから)
                var findEl = root.SelectSingleNode($"//testB/testTag[text()='{value1}']");

                Console.WriteLine("SelectSingleNode結果 XMLタグ名:" + findEl?.Name);
                Console.WriteLine("属性testAttr:" + findEl?.GetAttribute("testAttr"));

                // xml保存
                doc.Save(Path.Combine(Environment.CurrentDirectory, "test.xml"));
            }
            catch(Exception err)
            {
                Console.WriteLine(err.Message);                
            }
            finally
            {
                Console.Read();
            }
        }
    }
}

image.png

<?xml version="1.0" encoding="utf-8"?>
<root>
  <testA>
    <testB>
      <testTag testAttr="testAttrValue">valueTag1</testTag>
      <testTag>valueTag2</testTag>
    </testB>
  </testA>
</root>

おわりに

ラップせずともXmlElementを継承して実現できるのであれば、各メソッドやプロパティを実装しなくてよいのですが・・・

アドバイス等ありましたら、いただけると幸いです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0