inohilog

/var/log/inohiro.log

SessionとかViewStateの外部ファイルへの書き出し

XmlSerializerを使えば良かったのでしょうか。書いた後に気がついた。

自分で書いた版

コード
using System;
using System.IO;
using System.Text;

namespace SessionSave
{
	public partial class _Default : System.Web.UI.Page
	{
		protected void Page_Load( object sender, EventArgs e )
		{
			Session["data"] = "Hello";
		}

		protected void saveBt_click( object sender, EventArgs e )
		{
			string filePath = String.Format( Server.MapPath( @"~/App_Data/" ) + Session.SessionID + ".xml" );

			StringBuilder xml = new StringBuilder();
			xml.AppendLine( "<?xml version=\"1.0\" encoding=\"utf-8\" ?>" );
			xml.AppendLine( String.Format( "<session id=\"{0}\">", Session.SessionID ) );
			for( int i = 0; i < Session.Keys.Count; i++ )
				xml.AppendLine( String.Format( "<data name=\"{0}\">{1}</data>", Session.Keys[i].ToString(), Session[i].ToString() ) );
			xml.AppendLine( "</session></xml>" );

			string forWrite = xml.ToString();

			using( StreamWriter writer = new StreamWriter( filePath ) )
				writer.Write( forWrite );
		}
	}
}
結果
<?xml version="1.0" encoding="utf-8" ?>
<session id="te4sqginiw4xok552mjogo45">
<data name="data">Hello</data>
</session></xml>

System.Xml.Serialization.XmlSerializer を使った場合

とりあえずSessionをそのまま(System.Web.SessionState.HttpSettionState型として)Serializerにつっこんでみる。しかし、

XMLシリアル化を実現するには。ICollectionから継承される型は継承階層のすべてのレベルでAdd(System.Object)が実装される必要があります。System.Web.SettionState.HttpSettionStateにはAdd(System.Object)が実装されていません。」

とのことなので、OrderedDictionary(要素の順序が勝手に変わらない)に入れ直してからつっこんでみます。しかし、

型System.Collection.Generics.OrderedDictionaryはIDictionaryを実装するため、サポートされません。

と例外が。ICollectionを実装していてもIDictionaryを実装しているとシリアル化できないようです。
とりあえずめんどくさいので、自分でKey(String型)とValue(String型)を持つクラスを付くって、Listを使うことにします。

コード
using System;
using System.IO;
using System.Xml.Serialization;
using System.Collections.Generic;
using System.Web.UI.MobileControls;

namespace SessionSave
{
	public class Set
	{
		public string Key { get; set; }
		public string Value { get; set; }

		public Set() { }
		public Set( string key, string value )
		{
			this.Key = key;
			this.Value = value;
		}
	}

	public partial class _Default : System.Web.UI.Page
	{
		protected void Page_Load( object sender, EventArgs e )
		{
			Session["data"] = "Hello";
		}

		protected void saveBt_click( object sender, EventArgs e )
		{
			string filePath = String.Format( Server.MapPath( @"~/App_Data/" ) + Session.SessionID + ".xml" );

			List<Set> col = new List<Set>();
			for( int i = 0; i < Session.Keys.Count; i++ )
				col.Add( new Set( Session.Keys[i].ToString(), Session[i].ToString() ) );

			XmlSerializer serializer = new XmlSerializer( typeof( List<Set> ) );
			FileStream stream = new FileStream( filePath, FileMode.CreateNew );
			serializer.Serialize( stream, col );
		}
	}
}

これでちゃんとシリアル化できるんですが、クラスSetに「引数なしコンストラクタ」を定義しておかないと、

SessionSave.Setにはパラメータを持たないコンストラクタが含まれていないため、これをシリアル化することはできません。

と怒られます。

結果
<?xml version="1.0"?>
<ArrayOfSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Set>
    <Key xsi:type="xsd:string">data</Key>
    <Value xsi:type="xsd:string">Hello</Value>
  </Set>
</ArrayOfSet>

まとめ