オブジェクトの3分シリアライズ(XmlSerializer)
アプリケーションを作成する際に設定ファイルなんかをXMLファイルに定義する場合、みなさんはどのようにXMLのシリアライズクラスを作成しますか?
手順的には、
こんな順番でしょうか。
まぁ、先にXMLファイルを作るのかシリアライズクラスを作るのかケースバイケースですが、今回はこの順番で作成する場合に、手順2.を簡単に実装できますよ
というお話です。
シリアライズクラスを作成する
例えば、以下の様なXMLファイルがあったとします。
<?xml version="1.0"?> <MessageConfigration> <MessageDatas> <MessageData> <ID>E00001</ID> <Type>Error</Type> <Message>予期せぬエラーが発生しました。</Message> </MessageData> <MessageData> <ID>E00002</ID> <Type>Error</Type> <Message>メモリー容量が不足しています。再度実行してください。</Message> </MessageData> <MessageData> <ID>E00003</ID> <Type>Error</Type> <Message>ファイルの操作に失敗しました。</Message> </MessageData>
</MessageDatas>
</MessageConfigration>
よくあるメッセージの情報を定義したXMLファイルです。
このシリアライズクラスを作成する場合、まず思いつくのはルートのクラスを作成し、必要なプロパティを設定、それぞれのプロパティに属性を設定していく…
と言った方法でしょう。
多くのブログでもそのような記述を見かけます。
この方法だと、上記のような簡単な構造ならいいですが、更に複雑な構造に変更した際に無駄に工数を重ねることになります。
この問題を解決するために、MicrosoftはVisual Studioに便利な機能をのせています。
この機能を使用すると、XMLファイルの内容からシリアライズ可能なクラスを自動生成してくれます。
VSの機能を使ってシリアライズクラスを自動生成する
(2) シリアライズクラスを作成する。
⇒今回は、"MessageConfigration.Auto.cs"を作成
(3) メニューバー[編集]-[形式を指定して貼り付け]-[XMLをクラスとして貼り付ける] を選択する。
はい。たったこれだけです。
それではここで手順(2)から(3)の結果を見てみましょう。
ここに適当に作ったクラスファイルがあるじゃろ?( ^ω^) ⊃
これをこうして( ^ω^) ⊃
こうじゃ( ^ω^) ⊃
できました~(^ω^)
あとは、コメント追加したりリファクタリングしたりしましょう。
<MessageConfigration.Auto.cs>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; namespace XmlSerializerSample.Configrations { /// <summary> /// メッセージ設定ファイルの情報を定義します。 /// </summary> [XmlTypeAttribute(AnonymousType = true)] [XmlRootAttribute(Namespace = "", IsNullable = false)] public partial class MessageConfigration { #region Fields /// <summary> /// メッセージデータリスト /// </summary> private MessageData[] messageDatasField; #endregion //Fields #region Properties /// <summary> /// メッセージデータリスト /// </summary> [XmlArrayItemAttribute("MessageData", IsNullable = false)] public MessageData[] MessageDatas { get { return this.messageDatasField; } set { this.messageDatasField = value; } } #endregion //Properties } }
<MessageConfigration.cs>
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using XmlSerializerSample.Xml; namespace XmlSerializerSample.Configrations { /// <summary> /// メッセージ設定ファイルの情報を定義します。 /// </summary> public sealed partial class MessageConfigration { #region Static Fields /// <summary> /// 規定のファイル名 /// </summary> public static readonly string FileName = "Message-Config.xml"; /// <summary> /// 規定のファイルパス /// </summary> public static readonly string FilePath = Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FileName); /// <summary> /// カレントデータ /// </summary> private static MessageConfigration _current = null; #endregion //Static Fields #region Ctor /// <summary> /// コンストラクタ /// </summary> public MessageConfigration() { } #endregion //Ctor #region Static Properties /// <summary> /// カレントデータを取得します。 /// </summary> public static MessageConfigration Current { get { return _current; } private set { _current = value; } } #endregion //Static Properties #region Static Methods /// <summary> /// ファイルからデータを読み込みます。 /// </summary> /// <param name="filePath">ファイルパス(空文字の場合、規定のファイルパスから読み込みます。)</param> public static void Load(string filePath = "") { if (string.IsNullOrEmpty(filePath)) { filePath = FilePath; } Current = filePath.Deserialize<MessageConfigration>(); } /// <summary> /// カレントデータをファイルへ保存します。 /// </summary> /// <param name="filePath">ファイルパス(空文字の場合、規定のファイルパスから読み込みます。)</param> public static void Save(string filePath = "") { if (string.IsNullOrEmpty(filePath)) { filePath = FilePath; } Current.Serialize<MessageConfigration>(filePath); } #endregion //Static Methods } }
<MessageData.Auto.cs>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Xml.Serialization; namespace XmlSerializerSample.Configrations { /// <summary> /// メッセージデータの情報を定義します。 /// </summary> [XmlTypeAttribute(AnonymousType = true)] public partial class MessageData { #region Fields /// <summary> /// ID /// </summary> private string _id; /// <summary> /// 種別名 /// </summary> private string _type; /// <summary> /// メッセージ /// </summary> private string _message; #endregion //Fields #region Properties /// <summary> /// ID /// </summary> public string ID { get { return this._id; } set { this._id = value; } } /// <summary> /// 種別名 /// </summary> public string Type { get { return this._type; } set { this._type = value; } } /// <summary> /// メッセージ /// </summary> public string Message { get { return this._message; } set { this._message = value; } } #endregion //Properties } }
<MessageData.cs>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace XmlSerializerSample.Configrations { /// <summary> /// メッセージデータの情報を定義します。 /// </summary> public partial class MessageData { #region Properties /// <summary> /// メッセージ種別 /// </summary> public MessageType MessageType { get { MessageType result; return Enum.TryParse<MessageType>(Type, out result) ? result : default(MessageType); } } #endregion //Properties } }
今回はシリアライズクラスから直接読み込み、書き込みを行えるようにしました。
また、自動生成ファイル(*.Auto.cs)と手動ファイル(*.cs)ファイルを分離することにより、保守性も高めます。
今後この設定ファイルに項目を追加した場合は、*.Auto.cs ファイルを変更するだけで読み込み/書き込み処理の変更は不要です。
あとは必要に応じてデータのチェック処理を追加すればそこそこ使えるものになるんじゃないでしょうか。
今回作成したサンプルはこちら↓↓↓