none
自分自身をデシリアライズするクラスを作るには RRS feed

  • 質問

  • お世話になってます。
    早速ですが質問させて下さい。

    自分自身をデシリアライズするクラスを作成しています。
    以下のように記述したのですがthisが読み取り専用の為実現出来ません。

    public class MyClass {
      public string Text;
      public void DeserializeXml(string xmlText) {
        byte[] buffer = Encoding.Unicode.GetBytes(xmlText);
    
        XmlSerializer serializer = new XmlSerializer(this.GetType());
        using (MemoryStream ms = new MemoryStream(buffer)) {
          this = serializer.Deserialize(ms);  //thisが読み取り専用なので指定できない
        }
      }
    }
    何か良い方法があればどうぞご教示下さいますようお願い致します。
    2009年10月28日 17:52

回答

  • なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。

    単に MyClass のインスタンスを XML デシリアライズより生成するなら、 Factory クラスを定義し、こちらに生成責任を持たせるというのも手です。
    返信ありがとうございます。
    書いてる間に新しい返信を頂いてました。

    Factoryクラスを使用した方法ですとMyClassを継承する度に生成クラスを作る事になるのではと思っていますがいかがでしょう?
    (MyClassに対応するMyClassFactory
    MyChildAClassに対応するMyChildClassAFactory
    MyChildBClassに対応するMyChildClassBFactory
    ・・・・・)

    ”ではType指定出来るようにして汎用性があるようにすればよいのでは?”と言われますと
    先に書いたようにタイプセーフでない。他のクラスでも使えてしまう。
    というような事が起きてしまいます。


    ジェネリックかつタイプセーフにする手もあります。
    たとえば以下のクラスなら MyClass を継承したクラスのみ型パラメータに指定できます。

    // MyClass の Factory クラス
    public class MyClassFactory<T> where T : MyClass
    {
        public string Text;
        public static T Create(string xmlText)
        {
            byte[] buffer = Encoding.Unicode.GetBytes(xmlText);
    
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                return (T)serializer.Deserialize(ms);  //thisが読み取り専用なので指定できない
            }
        }
    }
    

     以下、使用例です。

    private void button1_Click(object sender, EventArgs e)
    {
    
        string xmlText = String.Empty;
        MyClass myClass = MyClassFactory<MyClass>.Create(xmlText);
    }
    
    • 回答としてマーク __test__ 2009年10月29日 3:31
    2009年10月29日 2:44
    モデレータ

すべての返信

  • 自分自身をデシリアライズするクラスを作成しています。
    以下のように記述したのですがthisが読み取り専用の為実現出来ません。
    this に代入することはできません。
    似たようなことを実現するのであれば、1度、ローカル変数に代入して、各メンバー変数をご自身でコピーするコードを書くといったような案もあります。

    【イメージ】
    MyClass temp = デシリアライズ;
    this.Text = temp.Text;
    (必要なだけ続く)
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    2009年10月28日 22:26
    モデレータ
  • う~ん

    public class MyClass{
      public string Text{ get; set; }
      public static MyClass DeserializeXml( string xmlText ){
        var reader = new StringReader( xmlText );
        var serializer = new XmlSerializer( typeof(MyClass) );
        return (MyClass)serializer.Deserialize( reader );
      }
    }

    使い方を考えるとわかります。元のコードですと
    var x  = new MyClass();
    x.DesrializeXml( someText );
    のはずです。
    ですが、1行目が問題です。目的なくインスタンスを生成してはいけません。
    そのため、
    var x  = MyClass.DesrializeXml( someText );
    とするのが一般的です。
    2009年10月28日 22:30
  • thisに代入されようとしていますので、自分自身のインスタンスを復元されたいのでしょうか? もしそうであれば、デシリアライズはインスタンスまで復元するわけではないので、結局ディープコピーになってしまいます。つまり元々のインスタンスとは異なるインスタンスが生成されてしまいます。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://blogs.wankuma.com/trapemiya/
    2009年10月29日 1:07
    モデレータ
  • なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。

    単に MyClass のインスタンスを XML デシリアライズより生成するなら、 Factory クラスを定義し、こちらに生成責任を持たせるというのも手です。

    // MyClass の定義
    public class MyClass
    {
        // ・・・・・・
    }
    
    // MyClass の Factory クラス
    public class MyClassFactory
    {
        public string Text;
        public static MyClass Create(string xmlText)
        {
            byte[] buffer = Encoding.Unicode.GetBytes(xmlText);
    
            XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                return (MyClass)serializer.Deserialize(ms); 
            }
        }
    }
    


    以下、使用例です。
    private void button1_Click(object sender, EventArgs e)
    {
        string xmlText = "XMLテキスト";
        MyClass myClass = MyClassFactory.Create(xmlText);
    }
    
    2009年10月29日 1:30
    モデレータ
  • 【イメージ】
    MyClass temp = デシリアライズ;
    this.Text = temp.Text;
    (必要なだけ続く)
    返信、ありがとうございます。
    おっしゃるとおりだと思います。
    私も当初、プロパティコピーメソッドを作り一つ一つコピーしてました。
    だたあまりにプロパティが多く一つずつ書くのに嫌気がさしてしまいまして・・。
    そこで自分自身を永続化し一発でコピー出来たらと考えたのがこの方法だったのです。
    2009年10月29日 1:44
  • 使い方を考えるとわかります。元のコードですと
    var x  = new MyClass();
    x.DesrializeXml( someText );

    のはずです。
    ですが、1行目が問題です。目的なくインスタンスを生成してはいけません。
    そのため、
    var x  = MyClass.DesrializeXml( someText );
    とするのが一般的です。
    返信ありがとうございます。
    まさに使い方はその通りです。
    私が想定している使い方を付け加えますと
    string src = MyClass.SrializeXml(); //シリアライズして状態をxml化した文字列で保存
    //色々と作業(MyClassのデータを保存したりxmlデータとして受け渡したり加工したり)
    //あるタイミングで復元
    MyClass.DesrializeXml(src);
    このような事を想定していました。

    おっしゃるようにデザパタのFactroyパターンのように
    MyClass obj = MyClass.DeserializeXml(xmlText);
    としてもよいのですがこれですと継承した場合thisが使えなくなる為

    public static MyClass DeserializeXml( string xmlText, Type objType ){
    ~略
    XmlSerializer serializer = new XmlSerializer(objType);
    このように引数が増え、型自身を指定する為タイプセーフでなくなります。
    またこの指定の仕方だとこのクラス自身でやる必要があるのかどうか・・・という疑問も出てきてしまった次第です。
    2009年10月29日 2:03
  • thisに代入されようとしていますので、自分自身のインスタンスを復元されたいのでしょうか?

    返信ありがとうございます。
    はい、その通りです。
    オブジェクトのデータを永続化し動作はディープコピーというような事をクラス自身に持たせたいのです。
    シリアライズ、デシリアライズをする別クラスを作り
    string src = MySerializer.Serialize(MyClassInstance);
    MyClass cls = (MyClass)MySerializer.Deserialize(xmlText, MyClass);
    このように使う事も考えたのですが”これだけならクラスに取り込みたいな・・・”とも思った次第です・・・。
    2009年10月29日 2:16
  • なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。

    単に MyClass のインスタンスを XML デシリアライズより生成するなら、 Factory クラスを定義し、こちらに生成責任を持たせるというのも手です。
    返信ありがとうございます。
    書いてる間に新しい返信を頂いてました。

    Factoryクラスを使用した方法ですとMyClassを継承する度に生成クラスを作る事になるのではと思っていますがいかがでしょう?
    (MyClassに対応するMyClassFactory
    MyChildAClassに対応するMyChildClassAFactory
    MyChildBClassに対応するMyChildClassBFactory
    ・・・・・)

    ”ではType指定出来るようにして汎用性があるようにすればよいのでは?”と言われますと
    先に書いたようにタイプセーフでない。他のクラスでも使えてしまう。
    というような事が起きてしまいます。
    2009年10月29日 2:27
  • なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。

    単に MyClass のインスタンスを XML デシリアライズより生成するなら、 Factory クラスを定義し、こちらに生成責任を持たせるというのも手です。
    返信ありがとうございます。
    書いてる間に新しい返信を頂いてました。

    Factoryクラスを使用した方法ですとMyClassを継承する度に生成クラスを作る事になるのではと思っていますがいかがでしょう?
    (MyClassに対応するMyClassFactory
    MyChildAClassに対応するMyChildClassAFactory
    MyChildBClassに対応するMyChildClassBFactory
    ・・・・・)

    ”ではType指定出来るようにして汎用性があるようにすればよいのでは?”と言われますと
    先に書いたようにタイプセーフでない。他のクラスでも使えてしまう。
    というような事が起きてしまいます。


    ジェネリックかつタイプセーフにする手もあります。
    たとえば以下のクラスなら MyClass を継承したクラスのみ型パラメータに指定できます。

    // MyClass の Factory クラス
    public class MyClassFactory<T> where T : MyClass
    {
        public string Text;
        public static T Create(string xmlText)
        {
            byte[] buffer = Encoding.Unicode.GetBytes(xmlText);
    
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                return (T)serializer.Deserialize(ms);  //thisが読み取り専用なので指定できない
            }
        }
    }
    

     以下、使用例です。

    private void button1_Click(object sender, EventArgs e)
    {
    
        string xmlText = String.Empty;
        MyClass myClass = MyClassFactory<MyClass>.Create(xmlText);
    }
    
    • 回答としてマーク __test__ 2009年10月29日 3:31
    2009年10月29日 2:44
    モデレータ
  • なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。

    単に MyClass のインスタンスを XML デシリアライズより生成するなら、 Factory クラスを定義し、こちらに生成責任を持たせるというのも手です。
    返信ありがとうございます。
    書いてる間に新しい返信を頂いてました。

    Factoryクラスを使用した方法ですとMyClassを継承する度に生成クラスを作る事になるのではと思っていますがいかがでしょう?
    (MyClassに対応するMyClassFactory
    MyChildAClassに対応するMyChildClassAFactory
    MyChildBClassに対応するMyChildClassBFactory
    ・・・・・)

    ”ではType指定出来るようにして汎用性があるようにすればよいのでは?”と言われますと
    先に書いたようにタイプセーフでない。他のクラスでも使えてしまう。
    というような事が起きてしまいます。


    ジェネリックかつタイプセーフにする手もあります。
    たとえば以下のクラスなら MyClass を継承したクラスのみ型パラメータに指定できます。

    // MyClass の Factory クラス
    public class MyClassFactory<T> where T : MyClass
    {
        public string Text;
        public static T Create(string xmlText)
        {
            byte[] buffer = Encoding.Unicode.GetBytes(xmlText);
    
            XmlSerializer serializer = new XmlSerializer(typeof(T));
            using (MemoryStream ms = new MemoryStream(buffer))
            {
                return (T)serializer.Deserialize(ms);  //thisが読み取り専用なので指定できない
            }
        }
    }
    

     以下、使用例です。

    private void button1_Click(object sender, EventArgs e)
    {
    
        string xmlText = String.Empty;
        MyClass myClass = MyClassFactory<MyClass>.Create(xmlText);
    }
    

    返信ありがとうございます。
    これは凄いですね。
    こんな書き方があるとは・・・。
    目から鱗が落ちました。

    これならタイプセーフかつ汎用的に使えますし生成部分を切り分けて別クラスにしたいとも思えました。
    シリアライズしたデータから生成、既存のオブジェクトからディープコピーしながら生成、etc...と夢が広がります。

    本当に助かりました。
    ありがとうございます。
    2009年10月29日 3:31
  • 目的は「ディープ コピー」なんですよね?

    
    [Serializable]
    public class MyClass : ICloneable
    {
    
        #region ICloneable メンバ
    
        public MyClass Clone() {
            using (MemoryStream ms = new MemoryStream()) {
                XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
                serializer.Serialize(ms, this);
                ms.Seek(0, SeekOrigin.Begin);
                return (MyClass)serializer.Deserialize(ms);
            }
        }
    
        object ICloneable.Clone() {
            return Clone();
        }
    
        #endregion
    }
    

     

    でもこれ、「パブリックで読み書き可能なプロパティ、またはフィールド」しか、コピーしませんよ。非公開のフィールドがあるなら、System.Runtime.Serialization を追加参照して使いましょう。ISerializable を定義しなければ、完全なコピーが作れます。

    
        public MyClass DeepClone() {
            using (MemoryStream ms = new MemoryStream()) {
                var bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
                bf.Serialize(ms, this);
                ms.Seek(0, SeekOrigin.Begin);
                return (MyClass)bf.Deserialize(ms);
            }
        }
    

     

    注意:コードは未確認。コンパイルのみ実行。


    Jitta@わんくま同盟
    2009年10月29日 13:32