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」で出力したものと若干違いますが、むしろ、こちらの方がフォーマットして、よさそうです。