XMLシリアル化可能クラスにパラメーターのないコンストラクターが必要な理由


173

Xmlシリアル化を行うコードを書いています。以下の機能付き。

public static string SerializeToXml(object obj)
{
    XmlSerializer serializer = new XmlSerializer(obj.GetType());
    using (StringWriter writer = new StringWriter())
    {
        serializer.Serialize(writer, obj);
        return writer.ToString();
    }
}

引数がパラメーターなしのコンストラクターのないクラスのインスタンスである場合、例外がスローされます。

未処理の例外:System.InvalidOperationException:CSharpConsole.Fooにはパラメーターのないコンストラクターがないため、シリアル化できません。System.Xml.Serialization.ModelScope.GetTypeModel(Type type、System.Xml.Serialization.TypeDesc.CheckSupported()でSystem.Xml.Serialization.TypeScope.GetTypeDesc(Type type、MemberInfo sourc e、Boolean directReference、Boolean throwOnError)でSystem.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(Type type、XmlRootAttribute root、String defaultNamespace)のSystem.Xml.Serialization.XmlSerializer..ctor(Type type、String defaultName space)のSystem.Xml.Serializationのブール値の直接参照)。 XmlSerializer..ctor(タイプタイプ)

XMLのシリアル化を成功させるために、パラメーターのないコンストラクターが必要なのはなぜですか?

編集:cfedukeの回答に感謝します。パラメーターなしのコンストラクターは、プライベートまたは内部にすることができます。


1
興味があれば、コンストラクターを使用せずにオブジェクトを作成する方法を見つけました(更新を参照)-しかし、これはXmlSerializerにはまったく役立ちません-要求されます 多分カスタムコードに便利です。
マークグラベル

1
XmlSerializer逆シリアル化には、デフォルトのパラメーターなしのコンストラクターが必要です。
Amit Kumar Ghosh、2015

回答:


243

オブジェクトのシリアル化解除中に、オブジェクトのシリアル化解除を担当するクラスは、シリアル化されたクラスのインスタンスを作成し、作成するインスタンスを取得してから、シリアル化されたフィールドとプロパティの設定に進みます。

あなたのコンストラクタを作ることができるprivateか、internalただ、それがパラメータなしだとして、あなたがしたい場合。


1
ああ、だから、私はパラメーターなしのctorをプライベートまたは内部にすることができ、シリアル化はまだ機能します。ご回答有難うございます。
モーガンチェン

2
はい、頻繁に使用しますが、ジェネリックと新しい初期化構文で "new()"を使用できるため、パラメーターなしのパブリックコンストラクターが優れていることを受け入れるようになりました。パラメーター化されたコンストラクターの場合は、静的ファクトリーメソッドまたはビルダーパターンの実装を使用します。
cfeduke、2008年

14
アクセシビリティのヒントは良いものですが、あなたの説明はシリアライズにとって意味がありません。オブジェクトは、逆シリアル化のためにのみ作成する必要があります。1つのインスタンスを両方の方法で使用できるため、型チェックコードがXmlSerializerコンストラクターに組み込まれていると思います。
Tomer Gabel、

7
@jwg 1つの例は、XMLを何らかのWebサービスに送信していて、独自のコンポーネントでそれらのオブジェクトを受信する必要がない場合です。
Tomer Gabel 2013

5
パラメータなしのコンストラクタprivateまたはを作成した場合でもinternal、値がシリアル化されたすべてのプロパティにはpublicセッターが必要であることに注意してください。
chrnola 2014年

75

これはの制限ですXmlSerializer。ただし、これ必要BinaryFormatterありDataContractSerializer ません。初期化されていないオブジェクトをイーサネットから作成し、逆シリアル化中に初期化できます。

xmlを使用しているのでDataContractSerializer、クラスを使用して[DataContract]/ [DataMember]でマークすることを検討する場合がありますが、これによりスキーマが変更されることに注意してください(たとえば、同等の[XmlAttribute]ものはなく、すべてが要素になります)。

更新:本当に知りたい場合は、コンストラクターを呼び出さずにオブジェクトを作成するためにBinaryFormatter使用FormatterServices.GetUninitializedObject()します。おそらく危険です。あまり頻繁に使用することはお勧めしません;-p MSDNの備考も参照してください。

オブジェクトの新しいインスタンスはゼロに初期化され、コンストラクターは実行されないため、オブジェクトは、そのオブジェクトによって有効と見なされる状態を表さない場合があります。現在のメソッドは、ユーザーがすぐにすべてのフィールドに入力するつもりである場合にのみ、逆シリアル化に使用する必要があります。不変タイプの空のインスタンスを作成しても意味がないため、初期化されていない文字列は作成されません。

独自のシリアル化エンジンを持っていますがFormatterServices、それを使用するつもりはありません。私は、コンストラクター(任意のコンストラクター)が実際に実行されたことを知るのがとても好きです。


FormatterServices.GetUninitializedObject(Type)に関するヒントをありがとう。:)
Omer van Kloeten 2009

6
へえ。私は自分のアドバイスに従わないことがわかりました。protobuf-netは(オプションで)年齢FormatterServices問わず
Marc Gravell

1
しかし、私が理解していないのは、コンストラクターが指定されていない場合、コンパイラーはパラメーターなしのパブリックコンストラクターを作成することです。それでは、なぜxmlの逆シリアル化エンジンにとって十分ではないのでしょうか。
toddmo、2015年

XMLをデシリアライズし、コンストラクターを使用して特定のオブジェクトを初期化する(要素/属性がコンストラクターを介して提供されるようにする)場合、これを実現する方法はありますか?シリアル化プロセスをカスタマイズして、コンストラクターを使用してオブジェクトを構築する方法はありませんか?
Shimmy Weitzhandler 2017年

1
@シミーいいえ; それはサポートされていません。あります IXmlSerializable、a:コンストラクターのに発生、b:非常に醜く、正しく理解することが困難です(特に逆シリアル化)-それを実装しようとしないことを強くお勧めしますが、コンストラクターを使用できません
マークグラベル

4

正解はありません。

その名前とは逆に、XmlSerializerクラスはシリアル化だけでなく、逆シリアル化にも使用されます。これは、クラスが機能することを確認するために特定のチェックを実行します。これらのチェックの一部は、逆シリアル化にのみ関係しますが、後で何をするつもりなのかわからないため、とにかくすべて実行します。

クラスがパスしないというチェックは、逆シリアル化にのみ関連するチェックの1つです。ここで何が起こるかです:

  • 逆シリアル化中に、XmlSerializerクラスはあなたのタイプのインスタンスを作成する必要があります。

  • 型のインスタンスを作成するには、その型のコンストラクターを呼び出す必要があります。

  • コンストラクターを宣言しなかった場合、コンパイラーはデフォルトのパラメーターなしコンストラクターをすでに提供していますが、コンストラクターを宣言した場合、それが使用可能な唯一のコンストラクターです。

  • したがって、宣言したコンストラクターがパラメーターを受け入れる場合、クラスをインスタンス化する唯一の方法は、パラメーターを受け入れるコンストラクターを呼び出すことです。

  • ただし、XmlSerializerは、パラメーターなしのコンストラクター以外のコンストラクターを呼び出すことができません。これは、パラメーターを受け入れるコンストラクターに渡すパラメーターがわからないためです。そのため、クラスにパラメーターなしのコンストラクターがあるかどうかを確認し、ないため、失敗します。

したがって、XmlSerializerシリアル化に関連するチェックのみを実行するようにクラスが記述されている場合、クラスは合格します。シリアル化については、パラメーターなしのコンストラクターが必要になることはまったくないためです。

他の人がすでに指摘しているように、問題の迅速な解決策は、単にパラメーターのないコンストラクターを追加することです。残念ながら、これはダーティソリューションでもありreadonlyます。これは、コンストラクターパラメーターからメンバーを初期化できないためです。

これらすべてに加えて、XmlSerializerクラス、パラメーターなしのコンストラクターなしでクラスの逆シリアル化さえも可能にするような方法で記述されている可能性があります。必要なのは、「The Factory Method Design Pattern」(Wikipedia)を利用することだけです。その見た目から、MicrosoftはこのデザインパターンはDotNetプログラマーにはあまりにも高度すぎると判断しました。Microsoftによると、DotNetプログラマーはパラメーターのないコンストラクターを使用するほうがよいということです。


笑い、For no good reason whatsoever,それから続けXmlSerializer is not capable of invoking any constructor except a parameterless constructor, because it does not know what parameters to pass to constructors that accept parameters.て、コンストラクタに渡すパラメータがわからない場合、ファクトリに渡すパラメータをどのようにして知るのでしょうか。またはどの工場を使用しますか?このツールがもっと簡単に使用できるとは思えません。クラスを逆シリアル化し、デシリアライザにデフォルトのインスタンスを作成させ、タグ付けした各フィールドにデータを入力します。簡単です。
チャック

0

まず、これはドキュメンテーションに書かれていることです。これはメインフィールドではなくクラスフィールドの1つだと思います。デシリアライザがパラメータなしの構築なしでどのように構築するかを教えてください。

私はコンストラクタをプライベートにするための回避策があると思います。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.