iTunes エクスポートファイルをパース

iTunes・ライブラリのエクスポートで得られるXMLファイルをパースするためのC#のクラス群。
アルバム(Albumクラス)と曲(Tuneクラス) と<dict>要素をラップするXDictクラスを定義。

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Linq;
using System.Xml.Linq;
using System.Xml.XPath;

  class Tune
    {
      public long Id;
      public string Name;
      public string Artist;
      public int Track;
      public long Size;
      public TimeSpan Time;
      public string Kind;
      public string Album;

      public Tune(XDict xDict)
        {
          this.Id     = (long)xDict[&quot;Track ID&quot;];
          this.Name   = xDict[&quot;Name&quot;] as string;
          this.Size   = (long)xDict[&quot;Size&quot;];
          this.Time   = (TimeSpan)xDict[&quot;Total Time&quot;];
          this.Kind   = xDict[&quot;Kind&quot;] as string;

          this.Artist = xDict[&quot;Artist&quot;] != null ? xDict[&quot;Artist&quot;] as string : &quot;None&quot;;
          this.Album  = xDict[&quot;Album&quot;] != null ? xDict[&quot;Album&quot;] as string : &quot;&quot;;
          this.Track  = xDict[&quot;Track Number&quot;] != null ? (int)xDict[&quot;Track Number&quot;]  : 0;
       }
    }

  enum SortBy { Time,Track,Name,Size }
  
  class Album
    {
      private Comparison&lt;Tune&gt;[] CompareBy = new Comparison&lt;Tune&gt;[4]
        {
          (x,y) =&gt; (int)((x.Time - y.Time).Ticks), //再生時間でソート
          (x,y) =&gt; x.Track - y.Track,              //トラックナンバーでソート
          (x,y) =&gt; string.Compare(x.Name,y.Name),  //曲名でソート
          (x,y) =&gt; (int)(x.Size - y.Size)          //サイズでソート
        };

      public List&lt;Tune&gt; Tunes = new List&lt;Tune&gt;();
      public string Name;
      public string Artist;

      public Album(Tune tune)
        {
          this.Name = tune.Album;
          this.Tunes.Add(tune);
        }

      public Album(string name,string artist)
        {
          this.Name = name;
          this.Artist = artist;
        }

      public void Sort(SortBy b)
        {
          this.Tunes.Sort(this.CompareBy[(int)b]);
        }
    }

  class XDict
    {
      private Dictionary&lt;string,object&gt; Element = new Dictionary&lt;string,object&gt;();
      private Dictionary&lt;string,XDict&gt; Dict = new Dictionary&lt;string,XDict&gt;();
      private Dictionary&lt;string,IEnumerable&lt;XDict&gt;&gt; Dicts = new Dictionary&lt;string,IEnumerable&lt;XDict&gt;&gt;();

      ///&lt;summary&gt;キー一覧&lt;/summary&gt;
      public string[] Keys
        {
          get
            {
              return this.Element.Keys.ToArray();
            }
        }

      ///&lt;summary&gt;インデクサ&lt;/summary&gt;
      public object this[string key]
        {
          get
            {
              return this.Element.ContainsKey(key) ? this.Element[key] : null;
            }

          set
            {
              if(!Element.ContainsKey(key))
                Element[key] = value;
            }
        }

      ///&lt;summary&gt;要素数&lt;/summary&gt;
      public int Length
        {
          get
            {
              return this.Element.Count();
            }
        }

      ///&lt;summary&gt;コンストラクタ&lt;/summary&gt;
      public XDict(XElement el)
        {
          this.Parse(el);
        }

      public XDict(string file,string xpath)
        {
          if(!File.Exists(file))
            throw new Exception(&quot;ファイルが存在しません&quot;);

          var xRoot = XElement.Load(file);
          var xRootDict = xRoot.XPathSelectElement(xpath);
          
          this.Parse(xRootDict);
        }

      ///&lt;summary&gt;
      /// dict要素(XElement)を渡して、子要素を取得していき、
      /// メンバーフィールドのElementを埋めていきます。
      ///&lt;/summary&gt;
      private void Parse(XElement elm)
        {
          if(elm.Name.LocalName != &quot;dict&quot;)
            return;

          var elms = elm.Elements();
          int num = elms.Count();
          var enumerator = elms.GetEnumerator();

          for(int i = 0;i &lt; num;i += 2)
            {
              enumerator.MoveNext();
              string key = enumerator.Current.Value;

              enumerator.MoveNext();
              XElement el = enumerator.Current;
              string typename = el.Name.LocalName;
              
              if( typename == &quot;dict&quot; )
                {
                  this.Element[key] = this.Dict[key] = new XDict(el);
                }
              else if(typename == &quot;array&quot;)
                {
                  this.Element[key] = this.Dicts[key] = from e in el.Elements() select new XDict(e);
                }
              else
                {
                  object obj = null;
                  string val = el.Value;

                  switch(typename)
                    {
                    case &quot;integer&quot;:
                      {
                        switch(key)
                          {
                          case &quot;Track Number&quot;:
                            obj = int.Parse(val);
                            break;
                            
                          case &quot;Total Time&quot;:
                            obj = TimeSpan.FromMilliseconds(long.Parse(val));
                            break;

                          default:
                            obj = long.Parse(val);
                            break;
                          }
                      }
                      break;

                    case &quot;string&quot;:
                      obj = val;
                      break;

                    case &quot;date&quot;:
                      obj = DateTime.Parse(val);
                      break;

                    case &quot;true&quot;:
                    case &quot;false&quot;:
                      obj = bool.Parse(typename);
                      break;
                    }
                  
                  this.Element[key] = obj;
                }
            }
        }
    }