Ktouth Brand. on Web

け〜くんこと K.Ktouth のだらだらした日常と突発的に作るプログラムや読み物とかの雑多サイト



[2014年07月21日]

Catel の XmlSerializer を使ってシリアライズ

2014年07月23日 03:14更新 筆者:K.Ktouth

先日のコードを Catel のシリアライズ機能で実装してみたのがこちら。テンプレートについている各種定型文は省略しています。

[Catel.Runtime.Serialization.SerializerModifier(typeof(SyncProfileEntrySerializerModifier))]
public class SyncProfileEntry : ModelBase, IXmlSerializable
{
  [System.Xml.Serialization.XmlAttribute("Name")]
  public string Name
  {
    get { return GetValue<string>(NameProperty); }
    private set { SetValue(NameProperty, value); }
  }
  public static readonly PropertyData NameProperty = RegisterProperty("Name", typeof(string), null);

  [System.Xml.Serialization.XmlAttribute("LastUpdated")]
  public DateTime LastUpdated
  {
    get { return GetValue<DateTime>(LastUpdatedProperty); }
    private set { SetValue(LastUpdatedProperty, value); }
  }
  public static readonly PropertyData LastUpdatedProperty = RegisterProperty("LastUpdated", typeof(DateTime), DateTime.MinValue);
}

public class SyncProfileEntrySerializerModifier : Catel.Runtime.Serialization.SerializerModifierBase<SyncProfileEntry>
{
  public override void SerializeMember(Catel.Runtime.Serialization.ISerializationContext context, Catel.Runtime.Serialization.MemberValue memberValue)
  {
    if (memberValue.Name == "LastUpdated") { memberValue.Value = ((DateTime)memberValue.Value).ToString(); }
  }
}

テストコードはこちら。

using System.Xml.Linq;
using Catel.Runtime.Serialization;
using Catel.Runtime.Serialization.Xml;

[TestFixture]
class SyncProfileEntryTest
{
  [TestCase]
  public void シリアライズが正しく機能する()
  {
    var entry = AssertHelper.CreateSyncProfileEntry("Sample<KKK>", DateTime.Now.AddMinutes(-153.152546));
    using(var stream = new System.IO.MemoryStream())
    {
      var serializer = SerializationFactory.GetXmlSerializer();
      serializer.Serialize(entry, stream);

      stream.Seek(0, System.IO.SeekOrigin.Begin);
      var root = XDocument.Load(stream).Root;

      Assert.That(root.Name.LocalName, Is.EqualTo(typeof(SyncProfileEntry).Name));
      Assert.That(root.Attribute("Name").Value, Is.EqualTo(entry.Name));
      Assert.That(root.Attribute("LastUpdated").Value, Is.EqualTo(entry.LastUpdated.ToLocalTime().ToString()));

      stream.Seek(0, System.IO.SeekOrigin.Begin);
      var restore = new SyncProfileEntry();
      serializer.Deserialize(restore, stream);
      Assert.That(restore, Is.Not.Null);
      Assert.That(restore.Name, Is.EqualTo(entry.Name));
      Assert.That(restore.LastUpdated.ToString(), Is.EqualTo(entry.LastUpdated.ToString()));
    }
  }
}

コレでほぼ前回と同じような出力が可能になります。使うシリアライザが DataContractSerializer から Catel の XmlSerializer に変わっています。
DataContract との違いは「Catel 形式の公開プロパティは標準でシリアライズ対象(書き込みがprivateでも問題なし)」「System.Xml.Serialization.XmlAttribute 属性を名前付きで指定したプロパティは Xmlシリアライザが Xmlアトリビュートとして出力する」と言う点です。また、〜Mofifier クラスを定義することでシリアライズ時の値を暗号化したりフォーマット変更したりすることも出来ます。今回はなぜか DateTime の出力が「mm/dd/yyyy〜」の形式で固定されていた点を日本的な「yyyy/mm/dd〜」で記述するために利用しています。デシリアライズの方はそのままでも大丈夫なので省略してますし、この違いに目をつぶることが出来るならクラス定義自体無視できます。

こちらの方がシンプルでやりやすい。

……と思っていたんですが。
大きな落とし穴がありましたあせ