トップ回答者
自分自身をデシリアライズするクラスを作るには

質問
-
お世話になってます。
早速ですが質問させて下さい。
自分自身をデシリアライズするクラスを作成しています。
以下のように記述したのですが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が読み取り専用なので指定できない } } }
何か良い方法があればどうぞご教示下さいますようお願い致します。
回答
-
なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。
返信ありがとうございます。
単に 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
すべての返信
-
自分自身をデシリアライズするクラスを作成しています。
this に代入することはできません。
以下のように記述したのですがthisが読み取り専用の為実現出来ません。
似たようなことを実現するのであれば、1度、ローカル変数に代入して、各メンバー変数をご自身でコピーするコードを書くといったような案もあります。
【イメージ】
MyClass temp = デシリアライズ;
this.Text = temp.Text;
(必要なだけ続く)
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。 -
う~ん
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 );
とするのが一般的です。 -
なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。
単に 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); }
-
使い方を考えるとわかります。元のコードですと
返信ありがとうございます。
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);
このように引数が増え、型自身を指定する為タイプセーフでなくなります。
またこの指定の仕方だとこのクラス自身でやる必要があるのかどうか・・・という疑問も出てきてしまった次第です。 -
thisに代入されようとしていますので、自分自身のインスタンスを復元されたいのでしょうか?
はい、その通りです。
オブジェクトのデータを永続化し動作はディープコピーというような事をクラス自身に持たせたいのです。
シリアライズ、デシリアライズをする別クラスを作り
string src = MySerializer.Serialize(MyClassInstance);
MyClass cls = (MyClass)MySerializer.Deserialize(xmlText, MyClass);
このように使う事も考えたのですが”これだけならクラスに取り込みたいな・・・”とも思った次第です・・・。 -
なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。
返信ありがとうございます。
単に MyClass のインスタンスを XML デシリアライズより生成するなら、 Factory クラスを定義し、こちらに生成責任を持たせるというのも手です。
書いてる間に新しい返信を頂いてました。
Factoryクラスを使用した方法ですとMyClassを継承する度に生成クラスを作る事になるのではと思っていますがいかがでしょう?
(MyClassに対応するMyClassFactory
MyChildAClassに対応するMyChildClassAFactory
MyChildBClassに対応するMyChildClassBFactory
・・・・・)
”ではType指定出来るようにして汎用性があるようにすればよいのでは?”と言われますと
先に書いたようにタイプセーフでない。他のクラスでも使えてしまう。
というような事が起きてしまいます。 -
なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。
返信ありがとうございます。
単に 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
-
なぜ自分自身をデシリアライズしたいのか意図が見えませんし、かなり危険な考えに思えます。
返信ありがとうございます。
単に 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...と夢が広がります。
本当に助かりました。
ありがとうございます。 -
目的は「ディープ コピー」なんですよね?
[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@わんくま同盟