[C#] XDocument から XmlNavigater を使えるように拡張

巷では、Windows 8 RTM, Visual Studio 2012 RTM で盛り上がっていますが、しばらくは平常運転で。
ちなみに 90日試用版は以下からダウンロードできます。

Download Windows 8 Enterprise Evaluation
http://msdn.microsoft.com/ja-jp/evalcenter/jj554510.aspx

# 英語のページだけど、きちんと日本語版がダウンロードできます。2.4GB 程度です。
# MSDN のほうも Japanese 版が用意されています。

さて、本題の XmlDom のほうはテストルーチンから作っていきます。
内部的には XML文字列 -> XDocument -> XmlDocument -> XmlNavigator の経由で渡しています。
ちまちま XML文字列をパースするコードを書いてもいいのですが、パース自体はあまりスピードを問わないので。
# 実は、System.Xml.Linq は Silverlight に含まれていない(今は含まれている?)ので避けていたのですが、
# Siverlight 自体が終了しそうな雰囲気なので、XDocument を使うことに決定

■テストコードを書く

以前に書いた XNode, XmlNode 構築のコードを XML文字列に直します。
Assert.AreEqual 部分はそのまま動くはず…というか、動かないと駄目です。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
namespace TestXmlDom
{
    [TestClass]
    public class TestXmlDocument
    {
        [TestMethod]
        public void TestParse1()
        {
            string xml = "<root><name>masuda tomoaki</name></root>";
            var doc = new XmlDocument(xml);
 
            var q = new XmlNavigator(doc)
                .Where(n => n.TagName == "name")
                .FirstOrDefault();
 
            Assert.AreEqual("masuda tomoaki", q.Value);
        }
 
        [TestMethod]
        public void TestParse2()
        {
            string xml = @"
<root>
 <name>masuda</name>
 <name>yamada</name>
 <name>yamasaki</name>
</root>";
            var doc = new XmlDocument(xml);
            var q = new XmlNavigator(doc)
                .Where(n => n.TagName == "name")
                ;
             
            Assert.AreEqual(3, q.Count());
            Assert.AreEqual("masuda", q.First().Value);
        }
 
        [TestMethod]
        public void TestParse3()
        {
            string xml = @"
<root>
 <person id='1'>
  <name>masuda</name>
  <age>44</age>
 </person>
 <person id='2'>
  <name>yamada</name>
  <age>20</age>
 </person>
 <person id='3'>
  <name>tanaka</name>
  <age>10</age>
 </person>
</root>";
            var doc = new XmlDocument(xml);
            var q = new XmlNavigator(doc)
                .Where(n => n.Attrs["id"] == "2")
                .FirstOrDefault();
            Assert.AreEqual("person", q.TagName);
            // ExDoc記述
            Assert.AreEqual("yamada", q / "name");
            Assert.AreEqual("20", q / "age");
        }
    }
}

XNode じゃなくて、XmlNode を使う理由としては「q / “name”」という書き方ができるからです。
この部分はXElemnetを使って「q.Element(“name”).Value」と書いても同じです。

ただし、q 自体は XNode を返すので Element() を含んでいなくていまいち不便なんですよね。

■XDocument から XmlDoucmnet を構築する

インターフェースとして、XmlDocument を用意します。
単純に、XDocument の中身を XmlDocument に移し替えているだけです。

ざっとインターフェースを合わせていきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
namespace Moonmile.XmlDom
{
    public class XmlDocument : XmlNode
    {
        public XmlNode documentElement {
            get
            {
                if (this.Children.Count() > 0)
                {
                    return Children[0];
                }
                else
                {
                    return XmlNode.Empty;
                }
            }
        }
 
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public XmlDocument()
        {
        }
 
        /// <summary>
        /// コンストラクタ(XML文字列で初期化)
        /// </summary>
        /// <param name="xml"></param>
        public XmlDocument( string xml)
        {
            this.LoadXml(xml);
        }
 
        /// <summary>
        /// ファイルから構築する
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public XmlDocument Load(string path)
        {
            return this.Load(XDocument.Load(path));
        }
 
        /// <summary>
        /// XML文字列から構築する
        /// </summary>
        /// <param name="xml"></param>
        /// <returns></returns>
        public XmlDocument LoadXml(string xml)
        {
            StringReader sr = new StringReader(xml);
            return this.Load(XDocument.Load(sr));
        }
 
        /// <summary>
        /// XDocumentオブジェクトから構築する
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        public XmlDocument Load(XDocument doc)
        {
            var root = LoadXNode(doc.FirstNode);
            this.Children.Add(root);
            return this;
        }
 
        protected XmlNode LoadXNode(XNode node)
        {
            var nn = new XmlNode(node.TagName(), node.Value());
            if (node.NodeType == System.Xml.XmlNodeType.Element)
            {
                var el = node as XElement;
                foreach (var at in el.Attributes())
                {
                    nn.Attrs.Add(new XmlAttr(at.Name.ToString(), at.Value));
                }
                foreach (var n in el.Nodes())
                {
                    nn.Children.Add(LoadXNode(n));
                }
            }
            return nn;
        }
    }
}

という訳で、これで XML を自由に比較的自由に扱うことができました。
今度は HTML 文字列を扱えるように、HtmlNode, HtmlDocument へ移していきます。

カテゴリー: C#, XmlDom パーマリンク