いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

.NET クラスライブラリ探訪-030 (シリアライズ-3)(IDeserializationCallback)


今回は、デシリアライズ後にコールバックメソッドを呼ぶ処理の書き方です。


.Netではデシリアライズ後に特定の処理を行う為の方法が2種類あります。

  • IDeserializationCallbackを実装する
  • OnSerializedAttributeを利用する


今回は、IDeserializationCallbackインターフェースの実装についてです。
OnSerializedAttributeについては、次回記述します。


IDeserializationCallbackインターフェースには以下のメソッドが一つだけ定義されています。

    void OnDeserialization(object sender);


上記のメソッドの引数senderは、msdnには「現状実装されていません。」と記述されています。
実際、常にnullが設定されています。


このメソッドを定義しておくと、デシリアライズが完了した後でコールバックされます。


ただし、IDeserializationCallbackインターフェースを利用する上で一つ注意点があります。

このメソッドは、XmlSerializerでは呼ばれない。

XmlSerializer以外のもの、つまり、BinaryFormatterとSoapFormatterではよばれます。
特に、XmlSerializerを使っているからといってコンパイルエラーが出るわけではないので
注意が必要です。


以下、サンプルです。

using System;
using System.IO;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace SerializationSample
{
    class Program
    {
        static void Main(string[] args)
        {
            //
            // IDeserializationCallbackを利用した処理.
            //
            // IDeserializationCallbackを利用するとデシリアライズ後に
            // カスタム処理を実行することが出来るようになる.
            //
            Console.WriteLine("============ (IDeserializationCallback) ===========");
            ExecuteIDeserializationCallback();

            Console.WriteLine("Press any key to exit...");
            Console.ReadLine();
        }

        #region Helper Methods
        private static void ExecuteIDeserializationCallback()
        {
            //
            // 元データを構築.
            //
            var originalObject = new Data3 { Id = 1, Name = "Name-1" };

            //
            // XmlSerializerを利用してシリアライズ及びデシリアライズ.
            // コールバックメソッドは呼ばれない。
            //
            using (TextWriter writer = new StreamWriter("Data3-XML.xml", false, Encoding.GetEncoding("sjis")))
            {
                new XmlSerializer(originalObject.GetType()).Serialize(writer, originalObject);
            }

            using (TextReader reader = new StreamReader("Data3-XML.xml", Encoding.GetEncoding("sjis")))
            {
                var deserializedObject = new XmlSerializer(originalObject.GetType()).Deserialize(reader);
                foreach (PropertyInfo propInfo in deserializedObject.GetType().GetProperties())
                {
                    Console.WriteLine("{0}={1}", propInfo.Name, propInfo.GetValue(deserializedObject, null));
                }
            }

            Console.WriteLine(string.Empty);

            //
            // BinaryFormatterを利用してシリアライズ及びデシリアライズ.
            // コールバックメソッドが呼ばれる。
            //
            using (Stream writer = File.Open("Data3-DAT.xml", FileMode.Create))
            {                
                new BinaryFormatter().Serialize(writer, originalObject);
            }

            using (Stream reader = File.Open("Data3-DAT.xml", FileMode.Open))
            {
                var deserializedObject = new BinaryFormatter().Deserialize(reader);
                foreach (PropertyInfo propInfo in deserializedObject.GetType().GetProperties())
                {
                    Console.WriteLine("{0}={1}", propInfo.Name, propInfo.GetValue(deserializedObject, null));
                }
            }

            Console.WriteLine(string.Empty);

            //
            // SoapFormatterを利用してシリアライズ及びデシリアライズ.
            // コールバックメソッドが呼ばれる。
            //
            using (Stream writer = File.Open("Data3-SOAP.xml", FileMode.Create))
            {
                new SoapFormatter().Serialize(writer, originalObject);
            }

            using (Stream reader = File.Open("Data3-SOAP.xml", FileMode.Open))
            {
                var deserializedObject = new SoapFormatter().Deserialize(reader);
                foreach (PropertyInfo propInfo in deserializedObject.GetType().GetProperties())
                {
                    Console.WriteLine("{0}={1}", propInfo.Name, propInfo.GetValue(deserializedObject, null));
                }
            }
        }
        #endregion
    }

    #region Sample Classes
    //
    // System.Runtime.Selialization.IDeserializationCallbackインターフェースを
    // 実装している型。
    //
    // OnDeserializationメソッドは、デシリアライズが完了した後に呼ばれる。
    //
    // 尚、OnDeserializationメソッドは、何故かXmlSerializer経由だと呼ばれない。
    // BinaryFormatterなどのフォーマッター経由だと呼び出される。
    //
    // どうもIDeserializationCallbackはruntime serializerの場合しか動作しないようである。
    // (ex: BinaryFomatter, SoapFormatter)
    // XmlSerializerは、そうではないので動かないらしい。
    //
    // -- http://www.dotnetmonster.com/Uwe/Forum.aspx/dotnet-xml/471/IDeserializationCallback-and-XmlSerializer
    //
    // The IDeserializationCallback is only used by the runtime serializers, i.e.
    // the SoapFormatter and the BinaryFormatter. There isn't really any counter
    // part for the XmlSerializer.
    //
    [Serializable]
    public class Data3 : IDeserializationCallback
    {
        public int Id
        {
            get;
            set;
        }

        public string Name
        {
            get;
            set;
        }

        /// <summary>
        /// オブジェクトがデシリアライズされた後にコールバックされます。
        /// </summary>
        /// <param name="sender">現在実装されていないとの事 (by msdn)。必ずnullが設定されている。</param>
        void IDeserializationCallback.OnDeserialization(object sender)
        {
            //
            // わざと値を変更する.
            //
            Id += 100;
            Name = "Modified";
        }
    }
    #endregion
}

================================
過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。