- PR -

C# SoapFormatterでprivateフィールドをシリアライズする方法

投稿者投稿内容
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2007-11-20 16:22
【質問1】
privateな_mmフィールドはなぜシリアライズされないのでしょうか?

【質問2】
privateなままでシリアライズ対象に含める方法はありますか?

【現象】
下のソースコードを参照願います
・長さ(インチ単位)が含まれているデータセットをシリアライズしています。
・下記サイトに次のような説明があります。

...SoapFormatterクラスは、(何も指定しないデフォルト状態で使うと)すべてのフィールドがシリアライズの対象となる。...

第13回 オブジェクトをXMLでシリアライズ(5)
http://www.atmarkit.co.jp/fdotnet/easyxml/easyxml13/easyxml13_02.html

・ところが、Inchクラスの_mmフィールドをprivateにするとシリアライズされません。出力結果も"0,0,0"になってしまいます。publicにするとシリアライズされ正しいインチ数が出力されます。

【環境】
OS:WindowsVista + .NET Framework3.0 (C#)

【ソースコード】
private void button1_Click(object sender, EventArgs e)
{
// ***** データセットを作成します

// 表を定義します
DataTable t1 = new DataTable("表1");
Type type1 = typeof(Inch);
t1.Columns.Add("インチ", type1);
// 表をデータセットに格納します
DataSet ds1 = new DataSet("データセット1");
ds1.Tables.Add(t1);
// 表にデータを追加します
t1.Rows.Add(new Inch(100));
t1.Rows.Add(new Inch(200));
t1.Rows.Add(new Inch(300));
// 古いファイルを削除します
const string FILE_PATH = @"c:\data\test1.xml";
if (File.Exists(FILE_PATH)) File.Delete(FILE_PATH);

// ***** データセットをSOAP形式のファイルに保存します

FileStream sw = new FileStream(FILE_PATH, FileMode.Create);
SoapFormatter sf = new SoapFormatter();
sf.Serialize(sw,ds1);
sw.Close();

// ***** SOAP形式のファイルからデータセットを復元します

FileStream sr = new FileStream(FILE_PATH, FileMode.Open);
DataSet ds2 = sf.Deserialize(sr) as DataSet;
sr.Close();

// ***** データを取り出します
DataTable t2 = ds2.Tables["表1"];
Console.WriteLine((t2.Rows[0][0] as Inch).Value); // 3.94inch
Console.WriteLine((t2.Rows[1][0] as Inch).Value); // 7.87inch
Console.WriteLine((t2.Rows[2][0] as Inch).Value); // 11.8inch
}

// オリジナルな型(ミリ数をインチ数に変換)
public class Inch
{
private Inch() { }
public Inch(double mm) { _mm = mm; }
// 質問
// privateフィールドもシリアライズする方法は?
// publicにすると_mmがシリアライズされます
// privateにすると_mmがシリアライズされません
// public double _mm;
private double _mm;
public const double RATE = 25.4;
public double Value { get { return _mm / RATE; } }
}
}
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-11-20 16:39
Inchクラスに、
[Serializable]
をつけるとどうなります?

DataSetのSoapシリアライズ時の動作を把握してないのですが、
見ている感じでは、XMLシリアライズを行っているようです。
※[Serializable]がInchについていないので、本来ならシリアライズ自体で
 失敗するはずですが、DataSetは特殊なシリアライズ処理をしているのでしょう、多分。

[Serializable]をつけると、普通にSoapシリアライズできるようになりますので、
そっちを自動的に使ってくれるんじゃないかと思われます。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-11-20 22:29
 シリアライズについては、まゆりんさんと2年がかりでここに投稿したのですけど、見ていなかったのね。。。

 おもしろいね。同じような時期に会議室デビューして、同じように質問を繰り返していたのに、あなたは今も質問を繰り返している。私はおおよそ自分で解決できるようになった。この差はなんなんでしょうね。
 私が担当した .NET Framework の案件は1件だけなんですよ。
れい
ぬし
会議室デビュー日: 2005/11/01
投稿数: 346
投稿日時: 2007-11-20 22:57
引用:

Jittaさんの書き込み (2007-11-20 22:29) より:
 シリアライズについては、まゆりんさんと2年がかりでここに投稿したのですけど、見ていなかったのね。。。


自分で問題に直面するまで見ないのは普通ではないですか?
私もそうでしたし。
回答する気でいる人なら別ですが。

引用:

 おもしろいね。同じような時期に会議室デビューして、同じように質問を繰り返していたのに、あなたは今も質問を繰り返している。私はおおよそ自分で解決できるようになった。この差はなんなんでしょうね。
 私が担当した .NET Framework の案件は1件だけなんですよ。


開発にかける時間が同じとは限らないし、習熟に要する時間は人それぞれです。
掲示板の読み方、利用仕方も人それぞれです。
質問しないで開発できる方がいいですが、
質問しなくちゃ開発できない人がダメであるわけではありません。

また、差を述べる必要があるのでしたら、平均との差であるべきです。

平均がどのくらいかわからないとしても、
サンプル数がある程度なくては説得力のある材料ではありません。
(私の教えてた部下はみんな1年で〜までできるようになった、とか。)

Jittaさんが特別な場合も十分にありえますから、
自分一人との差を述べる必要は全くなく、
人によっては悪意を持って取る可能性を考えると、無いほうがよいと思います。

Jittaさんは自分が平均だと思っているわけではないですよね?

差を述べることで、問題に関する取り組み方など、
本質的問題を提起しているのだと思いますが、
よりわかりやすく、適切且つ親切な方法があると思います。
直接言うとか。

(私はそれはおせっかいだと思いますが。)

本当に純粋に差があることに興味があり、議論したいなら、
別にスレッドを立てるべきでしょう。
ひろし
ぬし
会議室デビュー日: 2002/09/16
投稿数: 390
お住まい・勤務地: 兵庫県
投稿日時: 2007-11-21 15:03
ご回答ありがとうございます。

 [Serializable]をつけてprivateフィールドとpublicフィールドを比較しました。
DataSetの振る舞いが特別なのかを検証したところ、ご指摘の通りでした。ついでにDataTableについても調べましたが同様でした。

【テスト結果】
・通常のクラス(Inchクラス単体)はprivateフィールドも含めて正常にシリアライズされました。
・DataSetとDataTableについてはprivateフィールドがシリアライズされませんでした。
・XMLファイルの中身を確認しましたが、上記の事実を裏付ける内容でした。
・シリアライザをSoap形式からBinary形式に変更しましたが、残念ながら同様の結果でした。

【システムコンソールの出力】
開始
...中略...
Inchクラスのデータの確認
Inch (public)= 3.94 (private)= 3.94
...中略...
DataTableのデータの確認
Inch (public)= 3.94 (private)= 0.00
Inch (public)= 7.87 (private)= 0.00
Inch (public)=11.81 (private)= 0.00
...中略...
DataSetのデータの確認
Inch (public)= 3.94 (private)= 0.00
Inch (public)= 7.87 (private)= 0.00
Inch (public)=11.81 (private)= 0.00

【ソースコード】(少し長いですが...)
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine("開始");

// ***** シリアライザの作成
SoapFormatter fm = new SoapFormatter(); // SOAP形式
// BinaryFormatter fm = new BinaryFormatter(); // Binary形式

// ***** classを作成します。
Inch c1 = new Inch(100);
Inch c2 = new Inch(200);
Inch c3 = new Inch(300);

// ***** classの永続化と復元
{
// 古いファイルを削除します
const string CLASS_PATH = @"c:\data\inch1.xml";
if (File.Exists(CLASS_PATH)) File.Delete(CLASS_PATH);
// ファイルに保存します
Console.WriteLine("Inchクラスの永続化");
FileStream sw = new FileStream(CLASS_PATH, FileMode.Create);
fm.Serialize(sw, c1);
sw.Close();
// ファイルから復元します
Console.WriteLine("Inchクラスの復元");
FileStream sr = new FileStream(CLASS_PATH, FileMode.Open);
Inch newc = fm.Deserialize(sr) as Inch;
sr.Close();
// データを取り出します
Console.WriteLine("Inchクラスのデータの確認");
Console.WriteLine(newc.Value); // 3.94inch
}

// ***** DataTableを作成します
DataTable t1 = new DataTable("表1");
Type type1 = typeof(Inch);
t1.Columns.Add("インチ", type1);
t1.Rows.Add(c1);
t1.Rows.Add(c2);
t1.Rows.Add(c3);
// ***** DataTableの永続化と復元
{
// 古いファイルを削除します
const string TABLE_PATH = @"c:\data\table1.xml";
if (File.Exists(TABLE_PATH)) File.Delete(TABLE_PATH);
// ファイルに保存します
Console.WriteLine("DataTableの永続化");
FileStream sw = new FileStream(TABLE_PATH, FileMode.Create);
fm.Serialize(sw, t1);
sw.Close();
// ファイルから復元します
Console.WriteLine("DataTableの復元");
FileStream sr = new FileStream(TABLE_PATH, FileMode.Open);
DataTable t2 = fm.Deserialize(sr) as DataTable;
sr.Close();
// データを取り出します
Console.WriteLine("DataTableのデータの確認");
Console.WriteLine((t2.Rows[0][0] as Inch).Value); // 3.94inch
Console.WriteLine((t2.Rows[1][0] as Inch).Value); // 7.87inch
Console.WriteLine((t2.Rows[2][0] as Inch).Value); // 11.8inch
}

// ***** Datasetを作成します
DataSet ds1 = new DataSet("データセット1");
ds1.Tables.Add(t1);
// ***** DataSetの永続化と復元
{
// 古いファイルを削除します
const string DATASET_PATH = @"c:\data\dataset1.xml";
if (File.Exists(DATASET_PATH)) File.Delete(DATASET_PATH);
// ファイルに保存します
Console.WriteLine("DataSetの永続化");
FileStream sw = new FileStream(DATASET_PATH, FileMode.Create);
fm.Serialize(sw, ds1);
sw.Close();
// ファイルから復元します
Console.WriteLine("DataSetの復元");
FileStream sr = new FileStream(DATASET_PATH, FileMode.Open);
DataSet ds2 = fm.Deserialize(sr) as DataSet;
sr.Close();
// データを取り出します
DataTable t2 = ds2.Tables["表1"];
Console.WriteLine("DataSetのデータの確認");
Console.WriteLine((t2.Rows[0][0] as Inch).Value); // 3.94inch
Console.WriteLine((t2.Rows[1][0] as Inch).Value); // 7.87inch
Console.WriteLine((t2.Rows[2][0] as Inch).Value); // 11.8inch
}
}

// オリジナルな型(ミリ数をインチ数に変換)
[Serializable]
public class Inch
{
private Inch() { }
public Inch(double mm) { _mm1 = mm; _mm2 = mm; }
// 質問
// privateフィールドもシリアライズする方法は?
// _mm1はシリアライズされます
// _mm2はシリアライズされません
public double _mm1;
private double _mm2;
public const double RATE = 25.4;
public string Value
{ get { return String.Format("Inch (public)={0,5:0.00} (private)={1,5:0.00}", _mm1 / RATE, _mm2 / RATE); } }
}

【お礼】
これまでのアドバイスのおかげで実現できたことが多々ありました。
本掲示板の皆さんにはとても感謝しています。
掲示板では「ご回答ありがとうございます」としか書きませんが、いつも本当に有り難く感じています。
rain
ぬし
会議室デビュー日: 2006/10/19
投稿数: 549
投稿日時: 2007-11-21 15:48
引用:

Jittaさんの書き込み (2007-11-20 22:29) より:
 シリアライズについては、まゆりんさんと2年がかりでここに投稿した



発掘してみたので貼っておきます。
Webサービスのパラメータについて
なちゃ
ぬし
会議室デビュー日: 2003/06/11
投稿数: 872
投稿日時: 2007-11-21 21:16
DataSetはどのシリアライズ方式でも結局WriteXmlと同様の形になるので、
駄目ってことですかね。
本当にバイナリでいいなら、RemotingFormatプロパティをBinaryにして、BinaryFormatterを使ったらいけるかもしれません。
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2007-11-22 07:55
引用:

れいさんの書き込み (2007-11-20 22:57) より:
引用:

Jittaさんの書き込み (2007-11-20 22:29) より:
 シリアライズについては、まゆりんさんと2年がかりでここに投稿したのですけど、見ていなかったのね。。。


自分で問題に直面するまで見ないのは普通ではないですか?
私もそうでしたし。
回答する気でいる人なら別ですが。



一通りタイトルに目を通して、自分の仕事に関係しそうなもの、この先使えそうなもの、純粋に楽しそうなものは、結果を追いかけます。
だって、他の人が、ワタシに代わって苦労して下さっているんですよ?利用させていただかないと、もったいないじゃないですか?!

今回の主因は属性が付いていないことでした。掘り返してくださったものを見ると、付けていないとダメ、と書いています。また、こっちを強調したいですが、MSDNライブラリにもそう書かれています。DataSetに放り込んだので、DataSetがリフレクションを使ってシリアライズの真似事をしてくれますが、単独でシリアライズしようとしたら、エラーになるんですよ。連載記事をもとにしていますが、前の回に「付けること」って書いてないですか?
また、その回だけでも、コードを見比べればSerialize属性に気づいてもおかしくないと思います。
このように、ウェブ会議室で質問する前に、やっておけば間違いに気付ける事があると考えます。で、そのほうが早く解決するでしょ?



私は自分が平均だとは思っていませんよ。平均まで届いていますか?そんなに評価していただけるとはありがたいです。
他人の評価が自己評価より高ければイヤミになりますね。これは気をつけます。ありがとうございます。

スキルアップ/キャリアアップ(JOB@IT)