- PR -

データ構造を変更した場合のデシリアライズって…

1
投稿者投稿内容
清華
ベテラン
会議室デビュー日: 2005/12/21
投稿数: 50
投稿日時: 2006-01-04 17:31
清華です、お世話になっています。

タイトルの通りデータ構造を変更した場合のデシリアライズついてです、初期バージョンで
コード:
class BloodyHot
{
    int Why;
    int it;
    int is;
    int so;
    int hot;
}


というクラスをシリアライズしたとします、方法はバイナリでもSoapでもXmlでもいいですが、画像等をシリアライズする場合も考えBinaryFormatterを使ったとします。

数週間後に機能向上のため
コード:
class BloodyHot
{
    int Why;
    int it;
    int is;
    int so;
    int hot;
    bool question;
}



これだけで前回のデータを受け継げませんよね?これの回避方法って無いですかね?
僕なりに考えたのが

  • Soap等を使い、デシリアライズでエラーが発生する場合に追加した項目のデータが存在するか調べ、存在していない場合は追加してもう一度デシリアライズを試す。
  • 構造体をHashtableに置き換えて使用する、これならば新しい値を追加するのも簡単ですし、存在するか調べて存在しない場合はデフォルトの値を追加すればよい、しかし使用時遅い、保守性が悪い。
  • 保存にだけハッシュテーブルを使用する。ISerializableを継承して保存時にハッシュテーブルに全部詰め込んで上記と同じような処理を行う。速度も読み込み時以外問題なさそうです(読み込み時もハッシュテーブルならさほど問題ないでしょう)ただ保存されるデータがハッシュテーブルという多少気味が悪いデータになるのが問題ですね。拡張性は高そうですが。
  • あきらめて一個ずつ丁寧に保存コード書いて、読み込み時に存在していないか調べ存在していない場合はデフォルトの値を設定する。


最後の二つがコードとして綺麗になると思いますが、皆さんの意見をお聞きしたいと思います、他の方法や、僕の方法の利点・欠点等挙げていただければ幸いです。

よろしくお願いします。

_________________
9uiet Design - http://quietdesign.rental.allinoneserver.net/
デザインにこだわったソフトの配布とプログラミングTipsの公開(予定)をしています。
9uiet Blog - http://seiga.blog44.fc2.com/
笑ったことやプログラミングのことなど書
清華
ベテラン
会議室デビュー日: 2005/12/21
投稿数: 50
投稿日時: 2006-01-04 18:50
保存時にハッシュに叩き込む方法試してみましたがどうもうまくいきません。
コード:
private static object GetObject(Hashtable hash,string name,object defaultValue)
{
	if(hash.ContainsKey(name))
		return hash[name];
	return defaultValue;
}

public Setting(SerializationInfo info, StreamingContext context)
{
	Hashtable hash = (Hashtable)info.GetValue("h",typeof(Hashtable));
	if(hash == null)
		return;
	SampleInt32 = (int)GetObject(hash,"SampleInt32",50);
	SampleString = (string)GetObject(hash,"SampleString","aaaaa");
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
	Hashtable hash = new Hashtable();
	hash.Add("SampleInt32",SampleInt32);
	hash.Add("SampleString",SampleString);
	info.AddValue("h",hash);
}


Hashtable hash = (Hashtable)info.GetValue("h",typeof(Hashtable));
の部分で読み込んだhashの最大数が0となってます。info.AddValue/info.GetValueでハッシュを保存するのは無理なようですね orz さてどうしたものか……
_________________
9uiet Design - http://quietdesign.rental.allinoneserver.net/
デザインにこだわったソフトの配布とプログラミングTipsの公開(予定)をしています。
9uiet Blog - http://seiga.blog44.fc2.com/
笑ったことやプログラミングのことなど書
清華
ベテラン
会議室デビュー日: 2005/12/21
投稿数: 50
投稿日時: 2006-01-04 19:02
ハッシュで突っ込む方法ですが Saveメソッドと Loadメソッド用意することで対処できますね、こんな感じで。
コード:
public void Save(Stream stream)
{
	Hashtable hash = new Hashtable();
	hash.Add("SampleInt32",SampleInt32);
	hash.Add("SampleString",SampleString);

	BinaryFormatter f = new BinaryFormatter();
	f.Serialize(stream,hash);
}

public void Load(Stream stream)
{
	BinaryFormatter f = new BinaryFormatter();
	Hashtable hash = (Hashtable)f.Deserialize(stream);
	SampleInt32 = (int)GetObject(hash,"SampleInt32",50);
	SampleString = (string)GetObject(hash,"SampleString","aaaaa");			
}



割と綺麗なコードになると思うのですがどうでしょうか?ただ入れ子構造になったときが大変ですね… namespaceのように . で区切ってやるか?何かいいアイディアないですかね?
sia
常連さん
会議室デビュー日: 2004/05/02
投稿数: 38
投稿日時: 2006-01-04 19:07
どうもです。

>保存時にハッシュに叩き込む方法試してみましたがどうもうまくいきません。
の状況ですが(詳細にドキュメント類をあさった訳ではないで一部勘違いもあるかもしれませんが・・・)、メンバのDeserialize用コンストラクタは後で呼ばれるようです。
したがって、上記のコードは動作しません。

その他代案らしき物です。
・.Net Framework2.0を使う。
 型変更に強いSerializerが追加されたようです。(詳しくは私自身もしらないのですが・・・)

・ISerializableを実装して自力でバージョニングする。
 #かなり泥臭いですが・・・。

 public void GetObjectData(...)
{
  info.AddValue(バージョン情報)
  その他保存コード
 }

protected Setting(SerializationInfo info, StreamingContext context)
{
info.GetValue(バージョン情報)
if(バージョン情報 > 1.0){
}
}

参考になれば幸いです。
清華
ベテラン
会議室デビュー日: 2005/12/21
投稿数: 50
投稿日時: 2006-01-05 08:59
引用:

の状況ですが(詳細にドキュメント類をあさった訳ではないで一部勘違いもあるかもしれませんが・・・)、メンバのDeserialize用コンストラクタは後で呼ばれるようです。
したがって、上記のコードは動作しません。


いえ、Loadメソッドで呼び出すようにすればコンストラクタ関係ないので、現在書きのようなコードに落ち着いています。

コード:
using System;
using System.IO;
using System.Collections;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
//using System.Runtime.Serialization.Formatters.Soap;

namespace seiga.quietdesign.Data
{
	/// <summary>
	/// use to keep some data of structure or etc
	/// </summary>
	[Serializable]
	public class Pocket
	{
		private Hashtable hash;

		/// <summary>
		/// Create new pocket instance
		/// </summary>
		public Pocket()
		{
			hash = new Hashtable();
		}

		/// <summary>
		/// Create new pocket instance from particular pocket
		/// </summary>
		/// <param name="target"></param>
		/// <returns></returns>
		public Pocket Clone(Pocket target)
		{
			Pocket p = new Pocket();
			p.hash = (Hashtable)target.hash.Clone();
			return p;
		}

		/// <summary>
		/// Save to the stream this pocket data
		/// </summary>
		/// <param name="stream"></param>
		public void Save(Stream stream)
		{
			BinaryFormatter f = new BinaryFormatter();
			//			SoapFormatter f = new SoapFormatter();
			f.Serialize(stream,hash);
		}

		/// <summary>
		/// Load from the stream this pocket data.
		/// </summary>
		/// <param name="stream"></param>
		public void Load(Stream stream)
		{
			BinaryFormatter f = new BinaryFormatter();
			//			SoapFormatter f = new SoapFormatter();
			hash = (Hashtable)f.Deserialize(stream);
		}

		/// <summary>
		/// Get value from this pocket data
		/// </summary>
		/// <param name="key"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		public object GetValue(string key,object defaultValue)
		{
			if(hash.Count==0||!hash.ContainsKey(key))
				return defaultValue;
			return hash[key];
		}

		/// <summary>
		/// Add value to this pocket data
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public void AddValue(string key,object value)
		{
			hash.Add(key,value);
		}

		/// <summary>
		/// Remove at this pocket data
		/// </summary>
		/// <param name="key"></param>
		public void Remove(string key)
		{
			hash.Remove(key);
		}

		/// <summary>
		/// Clear at this pocket data.
		/// </summary>
		public void Clear()
		{
			hash.Clear();
		}
	}

	/// <summary>
	/// use this interface when you want to support Pocket
	/// </summary>
	public interface IPocketable
	{
		/// <summary>
		/// Create Pocket class from the object
		/// </summary>
		/// <returns></returns>
		Pocket	ToPocket();

		/// <summary>
		/// Create the object from the pocket data
		/// </summary>
		/// <param name="p"></param>
		void	FromPocket(Pocket p);
	}
}



使用例
コード:
public Pocket ToPocket()
{
	Pocket p = new Pocket();
	p.AddValue("Height",Height);
	p.AddValue("TileImage",TileImage);
	p.AddValue("TileArea",TileArea);
	p.AddValue("WallpaperImage",WallpaperImage);
	p.AddValue("WallpaperArea",WallpaperArea);
	p.AddValue("TextFont",TextFont);
	p.AddValue("TextColor",TextColor);
	p.AddValue("TextArea",TextArea);
	return p;
}

public void FromPocket(Pocket p)
{
	Height = (int)p.GetValue("Height",0);
	TileImage = (Bitmap)p.GetValue("TileImage",0);
	TileArea = (Rectangle)p.GetValue("TileArea",0);
	WallpaperImage = (Bitmap)p.GetValue("WallpaperImage",0);
	WallpaperArea = (Rectangle)p.GetValue("WallpaperArea",0);
	TextFont = (Font)p.GetValue("TextFont",0);
	TextColor = (Color)p.GetValue("TextColor",0);
	TextArea = (Rectangle)p.GetValue("TextArea",0);
}

FileStream fs = new FileStream(something);
Pocket p = A.ToPocket();
p.Save(fs);
-------------------------------------------
Pocket p = new Pocket();
p.Load(fs);
AClass A = new AClass();
A.FromPocket(p);



ToPocketでPocketデータとしてハッシュに保存することによって入れ子もこれで表現できます、どうですかねこれ?

引用:

・.Net Framework2.0を使う。
 型変更に強いSerializerが追加されたようです。(詳しくは私自身もしらないのですが・・・)


使えればよいのですがね……しばらく購入予定は無いです、ダウンロードも大きすぎて……

引用:

・ISerializableを実装して自力でバージョニングする。
 #かなり泥臭いですが・・・。

 public void GetObjectData(...)
{
  info.AddValue(バージョン情報)
  その他保存コード
 }

protected Setting(SerializationInfo info, StreamingContext context)
{
info.GetValue(バージョン情報)
if(バージョン情報 > 1.0){
}
}


バージョン情報を保存して分岐する方法は思いつきませんでした。スマートですが、バージョンが増えれば増えるほど(仕様が変更あればあるだけ)分岐増えますね。

さてどうしたものか…

一応Pocketで作り始めたのでPocketで行きたいと思いますが、これに何かしら問題ありますかね?
1

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