XmlSerializerはオブジェクトをXML形式にシリアライズすることができる便利なシリアライザです。シリアライズできないクラスの場合は、IXmlSerializableを実装する必要があります。クラスのフィールドにリストのリスト(ArrayListのリストやList<T>のリスト)やIDictionary型等が含まれている場合はそのままではシリアライズできません。今回はクラスのフィールドにIDictionaryを実装した、SortedList<TKey,TVal>を含めているのでIXmlSerizableを実装したサンプルを掲載します。

確認環境

  • Windows Vista Enterprise Edition
  • 開発環境:Visual Studio 2008 Professional 英語版
  • .NET 3.5(.NET 2.0以上)

1. シリアライズ対象クラスの作成

Visual Studio 2008 を起動し、コンソールプロジェクトを作成します。名前はXmlSerializerTestとしました。

1.1 UserInfoクラスの作成

UserInfo.csクラスファイルを作成し、以下のように編集します。XmlSerialzerは規定ではパブリックなフィールドやプロパティを属性としてシリアライズします。シリアライズするプロパティやフィールドを要素とするか、属性とするか、名前を指定する場合などは、XmlElement,XmlAttributeで指定します。XmlRootを指定すると、ルート要素がシリアライズされるときに要素名を指定できます。サンプルに掲載されていませんが、配列の要素名を変更する場合はXmlArrayAttributeを指定します。詳細はMSDNドキュメントを参照願います。

namespace XmlSerializerTest
{
    [XmlRoot("user", Namespace="urn:Handcraft.UserInfo")]
    public class UserInfo
    {
        public UserInfo()
        {
            ApplicationName = "/";
            UserName = string.Empty;
            IsApproved = false;
            IsLockedOut = false;
            Comment = string.Empty;
            Email = string.Empty;
            CreationDate = MinumDateTime();
            LastActivityDate = MinumDateTime();
            LastLockoutDate = MinumDateTime();
            LastLoginDate = MinumDateTime();
            LastPasswordChangedDate = MinumDateTime();
            PasswordQuestion = string.Empty;
            PasswordAnswer = string.Empty;
            EncodedPassword = string.Empty;
            PasswordSalt = string.Empty;
        }
        /// <summary>アプリケーション名</summary>
        [XmlAttribute("applicationName")]
        public string ApplicationName { get; set; }
        /// <summary>ユーザID</summary>
        [XmlAttribute("name")]
        public string UserName { get; set; }
        /// <summary>承認済みか</summary>
        [XmlElement("approved")]
        public bool IsApproved { get; set; }
        /// <summary>ロックアウト中か</summary>
        [XmlElement("lockouted")]
        public bool IsLockedOut { get; set; }
        /// <summary>コメント</summary>
        [XmlElement("comment")]
        public string Comment { get; set; }
        /// <summary>email</summary>
        [XmlElement("email")]
        public string Email { get; set; }
        [XmlElement("creationDate")]
        public DateTime CreationDate { get; set; }
        /// <summary>最終活動日時</summary>
        [XmlElement("lastActivityDate")]
        public DateTime LastActivityDate { get; set; }
        /// <summary>最終ロックアウト日時</summary>
        [XmlElement("lastLockoutDate")]
        public DateTime LastLockoutDate { get; set; }
        /// <summary>最終ログイン日時</summary>
        [XmlElement("lastLoginDate")]
        public DateTime LastLoginDate { get; set; }
        /// <summary>最終パスワード変更日時</summary>
        [XmlElement("lastPasswordChangedDate")]
        public DateTime LastPasswordChangedDate { get; set; }
        /// <summary>パスワードの質問</summary>
        [XmlElement("passwordQuestion")]
        public string PasswordQuestion { get; set; }
        /// <summary>パスワードの答え</summary>
        [XmlElement("passwordAnswer")]
        public string PasswordAnswer { get; set; }
        /// <summary>パスワード</summary>
        [XmlElement("password")]
        public string EncodedPassword { get; set; }
        /// <summary>パスワードサルト</summary>
        [XmlElement("passwordSalt")]
        public string PasswordSalt { get; set; }

        public virtual MembershipUser CreateMembershipUser(string providerName, object providerUserKey)
        {
            return new MembershipUser(providerName, UserName, providerUserKey, Email, 
                                        PasswordQuestion, Comment, IsApproved, IsLockedOut, 
                                        CreationDate, LastLoginDate, LastActivityDate, 
                                        LastPasswordChangedDate, LastLockoutDate);
        }
        public virtual MembershipUser CreatemembershipUser(string providerName)
        {
            return CreateMembershipUser(providerName, null);
        }
        protected virtual DateTime MinumDateTime()
        {
            return new DateTime(1754, 1, 1, 0, 0, 0, 0);
        }
    }
}

 1.2 UserInfoListクラスの作成

UserInfoクラスをコンテナの要素として含むUserInfoList.csを作成し、以下のように編集しました。UserInfoListはIXmlSerializableを実装して、独自にシリアライズするようにしています。

namespace XmlSerializerTest
{
    [XmlRoot("users", Namespace = "urn:Handcraft.UserInfoList")]
    public class UserInfoList : IXmlSerializable
    {
        /// <summary>
        /// ユーザ情報を格納するコレクション
        /// キーにUserInfo.UserNameを使用する
        /// </summary>
        private SortedList<string, UserInfo> _users = new SortedList<string, UserInfo>();
        /// <summary>
        /// UserInfoを追加する
        /// </summary>
        /// <param name="userInfo"></param>
        public void AddUserInfo(UserInfo userInfo)
        {
            _users.Add(userInfo.UserName, userInfo);
        }
        public UserInfo GetUserInfo(string name)
        {
            return _users[name];
        }
        public bool RemoveUserInfo(string name)
        {
            return _users.Remove(name);
        }
        #region IXmlSerializable Membersの実装

        public System.Xml.Schema.XmlSchema GetSchema()
        {
            return null;
        }

        /// <summary>
        /// 独自でデシリアライズを行う
        /// </summary>
        /// <param name="reader"></param>
        public void ReadXml(System.Xml.XmlReader reader)
        {
            XmlSerializer s = new XmlSerializer(typeof(UserInfo));

            reader.Read();
            while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
            {
                UserInfo o = s.Deserialize(reader) as UserInfo;
                AddUserInfo(o);
            }
        }

        /// <summary>
        /// 独自でシリアライズを行う
        /// </summary>
        /// <param name="writer"></param>
        public void WriteXml(System.Xml.XmlWriter writer)
        {
            XmlSerializer s = new XmlSerializer(typeof(UserInfo));
            foreach (UserInfo val in _users.Values)
            {
                s.Serialize(writer, val);
            }
        }
        #endregion
    }
}

2 テストプログラムの作成

プロジェクト作成時に既定で作成されるProgram.csのMainを以下のように編集してテストプログラムを記述します。

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

namespace XmlSerializerTest
{
    class Program
    {
        static void Main(string[] args)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(UserInfoList));
            UserInfoList list = new UserInfoList();
            UserInfo info = new UserInfo();
            info.UserName = "test";
            list.AddUserInfo(info);
            info = new UserInfo();
            info.UserName = "kitakore";
            list.AddUserInfo(info);
            using (FileStream fs = new FileStream(@"D:\temp\test.xml", FileMode.Create))
            {
                serializer.Serialize(fs, list);
            }
            using (FileStream fs = System.IO.File.OpenRead(@"D:\temp\test.xml"))
            {
                UserInfoList o = serializer.Deserialize(fs) as UserInfoList;
                Console.WriteLine(o.GetUserInfo("test").UserName);
            }
            //そのほかいろいろ
              List<UserInfo> glist = new List<UserInfo>();
            glist.Add(info);
            XmlSerializer s = new XmlSerializer(typeof(List<UserInfo>));
            using (FileStream fs = new FileStream(@"D:\temp\test2.xml", FileMode.Create))
            {
                s.Serialize(fs, glist);
            }
            UserInfo[] arrayList = new UserInfo[1];
            arrayList[0] = info;
            XmlSerializer s1 = new XmlSerializer(typeof(UserInfo[]));
            using (FileStream fs = new FileStream(@"D:\temp\test3.xml", FileMode.Create))
            {
                s1.Serialize(fs, arrayList);
            }
        }
    }
}

test.xmlの内容は次のようになります。

<?xml version="1.0"?>
<users xmlns="urn:Handcraft.UserInfoList">
  <user xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" applicationName="/" name="kitakore" xmlns="urn:Handcraft.UserInfo">
    <approved>false</approved>
    <lockouted>false</lockouted>
    <comment />
    <email />
    <creationDate>1754-01-01T00:00:00</creationDate>
    <lastActivityDate>1754-01-01T00:00:00</lastActivityDate>
    <lastLockoutDate>1754-01-01T00:00:00</lastLockoutDate>
    <lastLoginDate>1754-01-01T00:00:00</lastLoginDate>
    <lastPasswordChangedDate>1754-01-01T00:00:00</lastPasswordChangedDate>
    <passwordQuestion />
    <passwordAnswer />
    <password />
    <passwordSalt />
  </user>
  <user xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" applicationName="/" name="test" xmlns="urn:Handcraft.UserInfo">
    <approved>false</approved>
    <lockouted>false</lockouted>
    <comment />
    <email />
    <creationDate>1754-01-01T00:00:00</creationDate>
    <lastActivityDate>1754-01-01T00:00:00</lastActivityDate>
    <lastLockoutDate>1754-01-01T00:00:00</lastLockoutDate>
    <lastLoginDate>1754-01-01T00:00:00</lastLoginDate>
    <lastPasswordChangedDate>1754-01-01T00:00:00</lastPasswordChangedDate>
    <passwordQuestion />
    <passwordAnswer />
    <password />
    <passwordSalt />
  </user>
</users> <password />
    <passwordSalt />
  </user>
</users>

3. その他の情報

XmlSerializerはシリアライズするクラスを動的にコンパイル処理を行うのにcsc.exeを使います。このとき、csc.exeが停止することがあるそうです。詳細は以下のリンク参照。回避方法としてプリコンパイルを行うようsgen.exeツールを使うようにします。

-XmlSerializer クラスから起動された csc.exe が一定時間応答を停止する場合がある 
http://support.microsoft.com/kb/899153/ja
-Windows Vista で XmlSerializer クラスを使用するアプリケーションの動作により、リムーバブル ディスクが安全に取り外せないことがある
http://support.microsoft.com/kb/957190/ja

XMLのシリアル化を細かく制御する属性については次のリンクを参照

XML シリアル化を制御する属性
http://msdn.microsoft.com/ja-jp/library/83y7df3e(VS.80).aspx

説明は以上です。間違い、誤り等があればご連絡ください。