C# - よく使うクラス - System.Runtime - Serialization.DataContractSerializer

クラウディア 
1. 概要
2. 定義
3. WriteObject
4. 工夫

1. 概要

 「System.Runtime.Serialization.DataContractSerializer」は、参考サイトによれば
ある型のインスタンスを、提供されたデータ コントラクトを使用して、XML ストリームまたはドキュメントにシリアル化または逆シリアル化します。 このクラスは継承できません。
 とのことです。  実は、内容をよく知らないのですが、大量のデータを扱うことがあって。  メモリが逼迫してきて、どれくらいでメモリを使うことになるのかと、その大量の項目を持つ、「List」をシリアル化して、バイナリ形式にして、サイズをはかっていたら。  一定の時間後に「System.Runtime.Serialization.SerializationException: 内部配列を Int32.MaxValue 要素を超えて展開することはできません。」てな例外が、発生しました。  これをキーに検索をかけると、「BinaryFormatter」だか何だかで、シリアル化できる数は、限界がすぐにくるそうで、この例外が発生するならば、「DataContractSerializer」を使え。  的な記事が、よくひっかかるのです。  そのうち、使う羽目に陥るのではないかと思っております。  これ、わたしの知る限りでは「Mono」には、ありません。  検索したところによると、同じ役割をするプログラムを書いている箇所がありました。  本ページは、下記のサイトを参考にさせていただきました。
DataContractSerializer クラス (System.Runtime.Serialization)」
「BinaryFormatter で大量データを扱うと例外が発生。」
「DataContractSerializer を使って、オブジェクトの XML シリアル化、逆シリアル化を行う」
「[C#][.NET][XML] オブジェクトのシリアライズ/デシリアライズ(DataContractSerializerの使用方法)

2. 定義

 これを使えるようにするには、少しおまじないがありまして、最初、それにとまどってうまく使えませんでした。

using System.Runtime.Serialization;
 の記述と、参照設定で

System.Runtime.Serialization
 を参照していないと、ビルド時に

CS0246: 型または名前空間名 'DataContractSerializer' が見つかりませんでした。using ディレクティブまたはアセンブリ参照が不足しています。
 等のエラーになります。

3. WriteObject

 「すべてのオブジェクト データ (XML の開始要素、コンテンツ、終了要素) を XML ドキュメントまたはストリームに書き込みます。」とのことです。  「XmlSerializer」で記述すると、下記のようなプログラム。

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace Console
{
	public class myClass
	{
		public long		i	= 0;												// 数値型要素
		public string	s	= String.Empty;										// 文字列型要素
	}

	class Program
	{
		static void Main(string[] args)
		{
		List<myClass> list = new List<myClass>
			{
				new myClass { i = 1, s = "A" },
				new myClass { i = 2, s = "B" },
				new myClass { i = 3, s = "C" },
			};

			using(FileStream fs = new FileStream("./tmp.xml", FileMode.Create, FileAccess.Write, FileShare.Write))
			{
				XmlSerializer xmlserializer = new XmlSerializer(typeof(List<myClass>));
				xmlserializer.Serialize(fs, list);
			}
		}
	}
}
 これをビルドして、実行すると、下記の内容を持つ、「.xml」ファイルを出力します。  ([Serializable] つけないと、うまく出力されないかと思いましたらば、この程度だとうまく出力してくれます)

<?xml version="1.0"?>
<ArrayOfMyClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <myClass>
    <i>1</i>
    <s>A</s>
  </myClass>
  <myClass>
    <i>2</i>
    <s>B</s>
  </myClass>
  <myClass>
    <i>3</i>
    <s>C</s>
  </myClass>
</ArrayOfMyClass>
 これ、27、28行を。

				DataContractSerializer serializer = new DataContractSerializer(typeof(List<myClass>));
				serializer.WriteObject(fs, list);
 と書きかえれば、ほぼ等価になるのですが(もちろん、「using」や参照設定は行うとして)。  出力は、下記のようになってしまいます。

<ArrayOfmyClass xmlns="http://schemas.datacontract.org/2004/07/Console" xmlns:i="http://www.w3.org/2001/XMLSchema-instance"><myClass><i>1</i><s>A</s></myClass><myClass><i>2</i><s>B</s></myClass><myClass><i>3</i><s>C</s></myClass></ArrayOfmyClass>
 つまり、1行に全部出力されてしまいます。

4. 工夫

 前項の状態では、人が読めないファイルになっています。  これを解消するには、ちょっとした工夫が必要です。  「XmlWriterSettings」「XmlWriter」を併用します。  すなわち、「using」の部分を下記のように変えて

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml;
 前項の、27、28行を。

			XmlWriterSettings settings = new XmlWriterSettings();
			settings.Indent = true;
			settings.Encoding = new System.Text.UTF8Encoding(false);

			using (XmlWriter xw = XmlWriter.Create("./tmp.xml", settings))
			{
				DataContractSerializer serializer = new DataContractSerializer(typeof(List<myClass>));
				serializer.WriteObject(xw, list);
			}
 と記述します。  ビルド後、実行すると、下記の結果が得られます。

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfmyClass xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/Console">
  <myClass>
    <i>1</i>
    <s>A</s>
  </myClass>
  <myClass>
    <i>2</i>
    <s>B</s>
  </myClass>
  <myClass>
    <i>3</i>
    <s>C</s>
  </myClass>
</ArrayOfmyClass>
 「XmlSerializer」で出力したものと若干違いますが、むしろ、こちらの方がフォーマットして、よさそうです。
AbemaTV 無料体験