連載 .NETで簡単XML 第13回 オブジェクトをXMLでシリアライズ(5)株式会社ピーデー 川俣 晶2004/01/21 |
|
|
これらの相違はなぜ起きているのだろうか。そして、それは何を意味しているのだろうか。
まず出力されたXML文書の内容の相違について見てみよう。「SOAP」という言葉を知っている読者なら、SoapFormatterというクラス名や、http://schemas.xmlsoap.org/soap/encoding/などの名前空間URIからうすうす察しが付くかもしれない。これは、XML Webサービスなどで使われるプロトコルであるSOAP(Simple Object Access Protocol)形式でシリアライズされた結果である。つまり、SoapFormatterクラスは、SOAPという仕様が存在することを前提にして、XmlSerializerクラスを使ったサンプル・ソースとは異なる目的と意図を持ったところで使われるべきものといえる。SOAPを前提としたデータのやりとりを行う場合は、XmlSerializerクラスではなく、SoapFormatterクラスを使用した方が便利という状況があるかもしれない。
さて、XmlSerializerクラスと、SoapFormatterクラスの相違を、SOAP形式にするか否かという相違だと理解すると、出力結果の4行目の相違が理解できない。実は、両者の間には、単純にSOAP形式か否かというような単純な相違ではなく、もっと本質的な相違が存在している。
すでに説明したように、XmlSerializerクラスによって行うシリアライズは、パブリック・フィールドとパブリック・プロパティのみを扱う。それに対して、SoapFormatterクラスは、(何も指定しないデフォルト状態で使うと)すべてのフィールドがシリアライズの対象となる。この条件には、パブリックであるか否かという条件はなく、また、プロパティは対象に含まれていない。つまり、この場合、4行の出力結果のうち3行は同じ内容の結果となっていたが、同じ結果の行も同じ動作の結果ではないケースがあるということである。XmlSerializerクラスを使った場合にプロパティ経由でシリアライズされていたm_nameの値は、SoapFormatterクラスではフィールド経由でシリアライズされていたわけである。また、初期値として設定した後、書き換える手段が用意されていないm_temporaryIDの値も、実はシリアライズ、デシリアライズを通じて保存されていたのである。
それらの証拠は、生成されたXML文書に現れている。要素名は、すべてm_nameのようなフィールド名になっていて、Nameのようなプロパティ名は現れていない。また、XmlSerializerクラスを使った場合には対象になっていなかったm_addressやm_temporaryIDも確かにXML文書に書き込まれていることが分かるだろう。
さて、すべてのフィールドが自動的に何もかもシリアライズされるとなると、困る状況もあり得るだろう。例えば、一時的に取得したハンドル番号など、永続的に保存しても何の意味もなく、かえってトラブルの元になるだけ、という情報もあり得る。それに対する抑止力的な意味を持つ属性が存在する。それが、Personクラスに付加されているSerializable属性(System名前空間)である。SoapFormatterクラスは、このSerializable属性が付加されたクラスのみをシリアライズ対象とする。つまり、SoapFormatterクラスでシリアライズしてもよいクラスにのみこの属性を付けることで、シリアライズされては困る状況に対処することができる。
もし、Serializable属性を取り去ってこのサンプル・プログラムを実行すると、以下のような例外が発生することになる。
|
|
Serializable属性がない場合に発生する例外 |
最後に、両者の間の小さな相違を指摘しておこう。XmlSerializerクラスを使った場合には、XmlSerializerクラスのコンストラクタにシリアライズする型の情報をGetType(Person)(C#ではtypeof(Person))として指定していた。しかし、SoapFormatterクラスを使った場合には、そのような引数を渡していない。これは、XmlSerializerクラスのインスタンスは、特定の型をシリアライズする専用品として生成されるのに対して、SoapFormatterクラスのインスタンスは扱う型が限定されない汎用品として生成されることを意味する。この相違は、異なる型を持つ複数のオブジェクトをまとめてシリアライズする場合に、コーディング上の差となって現れることになる。
シリアライズさせないフィールド
すでに述べたとおり、すべてのフィールドが無条件でシリアライズされると困る場合がある。このような場合は、Serializable属性を付けないことにより、SoapFormatterクラスによりシリアライズの対象から外すことができることはすでに述べた。
しかし、1つのフィールドがシリアライズされると困るからといって、クラスそのものがシリアライズ不可能になってしまっても、困る場合があるだろう。そのような状況に対処するために、XmlSerializerクラスを使う場合には、特定の項目をシリアライズ対象から外すXmlIgnore属性(System.Xml.Serialization名前空間)を使用する方法を第9回では解説した。
SoapFormatterクラスを使用する際にも、同じように特定のフィールドをシリアライズ対象から外すNonSerialized属性(System名前空間)を使用することができる。以下はそれを使用した例である。シリアライズされるクラス以外は、前の例と同じなので、シリアライズされるクラスのソース・コードのみ掲載する。
|
|
サンプル・プログラム2:NonSerialized属性を使用した場合のPersonクラス(VB.NET版/C#版) |
これを実行すると、以下のような出力が得られる。
|
|
サンプル・プログラム2の出力結果 |
また、同時に以下のような内容のXMLファイルが「c:\sample.xml」に生成される。
|
|
NonSerialized属性を使用した場合に生成されるXML文書 |
さて、出力されたXML文書を見ると、確かに、NonSerialized属性を付けたメンバ、m_temporaryIDが出力されていないことが分かるだろう。この属性を使うことで、シリアライズされて困るフィールドの出力を抑止することができる。効能は、XmlIgnore属性と同様だが、XmlSerializerクラスとSoapFormatterクラスは相互に関連のない独立したクラスであるため、別のNonSerialized属性を使う必要がある。
余談だが、出力結果の4行目が空行になっていることに注意しよう。クラスが初期化した値はデシリアライズ時にNothing(C#ではnull)で上書きされているわけである。シリアライズの対象にならないことと、デシリアライズに書き換えられないことはイコールではない。これはNonSerialized属性を使った場合とは異なる動作である。
INDEX | ||
.NETで簡単XML | ||
第13回 オブジェクトをXMLでシリアライズ(5) | ||
1.SoapFormatterを使ったシリアライズ | ||
2.シリアライズさせないフィールド | ||
3.SoapFormatterの姉妹クラス、BinaryFormatterクラス | ||
「連載 :.NETで簡単XML」 |
- 第2回 簡潔なコーディングのために (2017/7/26)
ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている - 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう - 第1回 明瞭なコーディングのために (2017/7/19)
C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える - Presentation Translator (2017/7/18)
Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
|
|