ShouldSerialize *パターンと* Specifiedパターンの両方、およびそれらがどのように機能するかを知っていますが、2つの間に違いはありますか?
特定の事柄を条件付きでシリアル化する必要があるときに、一方の方法と他方の方法を使用する「落とし穴」はありますか?
この質問はXmlSerializer
の使用に固有のものですが、このトピックに関する一般的な情報も歓迎します。
このトピックに関する情報は世の中にほとんどないので、それは彼らがまったく同じ目的を実行し、それがスタイルの選択だからです。ただし、.NETの実装者がリフレクションを介してクラスを分析し、どちらか一方または両方のパターンを探して生成されたシリアライザーの動作を決定するのは奇妙なようです。
EDIT:*Specified
プロパティまたはShouldSerialize*
メソッドのいずれかがtrueを返す場合、2つのパターンに不慣れな人は、そのプロパティがシリアル化されます。
public string MyProperty { get; set; }
//*Specified Pattern
[XmlIgnore]
public bool MyPropertySpecified { get{ return !string.IsNullOrWhiteSpace(this.MyProperty); } }
//ShouldSerialize* Pattern
public bool ShouldSerializeMyProperty()
{
return !string.IsNullOrWhiteSpace(this.MyProperty);
}
_{propertyName}Specified
_パターンの意図は XMLスキーマバインディングサポート:MinOccurs属性バインディングサポート に記載されています。次のようなXSDスキーマ要素をサポートするために追加されました。
<element>
_要素が関係しています。この場合、 _xsd.exe /classes
_ は、スキーマ要素と同じ名前のプロパティと_{propertyName}Specified
_ブール値のget/setプロパティ要素がXMLで検出され、XMLにシリアル化する必要があるかどうかを追跡します。要素が検出されると、_{propertyName}Specified
_はtrue
に設定され、それ以外の場合はfalse
に設定されます。したがって、逆シリアル化されたインスタンスは、元のXMLでプロパティが(デフォルト値に明示的に設定されたのではなく)設定解除されたかどうかを判断できます。
逆もスキーマ生成のために実装されています。上記のパターンに一致するプロパティのペアでC#タイプを定義し、_xsd.exe
_を使用して対応するXSDファイルを生成すると、適切なminOccurrs
がスキーマに追加されます。たとえば、次のタイプがあるとします。
_public class ExampleClass
{
[XmlElement]
public decimal Something { get; set; }
[XmlIgnore]
public bool SomethingSpecified { get; set; }
}
_
次のスキーマが生成され、その逆も同様です。
_<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="ExampleClass" nillable="true" type="ExampleClass" />
<xs:complexType name="ExampleClass">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Something" type="xs:decimal" />
</xs:sequence>
</xs:complexType>
</xs:schema>
_
_xsd.exe
_は、値型プロパティの_{propertyName}Specified
_プロパティを自動的に生成するためにのみ記述されていますが、XmlSerializer
は、参照型プロパティに手動で使用されたときにパターンを尊重することに注意してください。
この場合、_xsd.exe
_が_Nullable<T>
_にバインドしないのはなぜですか?おそらく:
xsi:nil="true"
_ 属性をサポートするために使用されます。 Xsi:nil Attribute Binding Support を参照してください。_xsd.exe
_が自動的に生成する場合があるため、このパターンに注意する必要がありますが、プロパティとそのSpecified
プロパティ間の相互作用は奇妙であり、バグを生成する可能性があります。対応するSpecified
プロパティをtrue
に設定していないため、クラスのすべてのプロパティを入力し、XMLにシリアル化してすべてを失う可能性があります。この「ゴッチャ」はここに時々現れます。 この質問 または この質問も 。
このパターンのもう1つの「問題」は、このパターンをサポートしないシリアライザーで型をシリアル化する必要がある場合、手動でmayシリアライゼーション中にこのプロパティの出力を抑制し、おそらくデシリアライゼーション中に手動で設定する必要があります。各シリアライザーには、プロパティを抑制するための独自のカスタムメカニズムがある(またはメカニズムがまったくない)場合があるため、これを行うと、時間の経過とともにますます負担が大きくなります。
(最後に、セッターなしでMyPropertySpecified
が正常に動作することに少し驚いています。{propertyName}Specified
_セッターがないと例外がスローされる.Net 2.0のバージョンを思い出しているようです。しかし、もはやそれ以降のバージョンでも再現可能で、テストする2.0がないので、3つ目の問題になるかもしれません。)
ShouldSerialize{PropertyName}()
メソッドのサポートは Windowsフォームコントロールのプロパティ:ShouldSerializeおよびResetメソッドによるデフォルト値の定義 に記載されています。ご覧のように、ドキュメンテーションはXmlSerializer
セクションではなく、MSDNのWindowsフォームセクションにあるため、実際にはセミ隠し機能です。このメソッドのサポートとSpecified
プロパティの両方がXmlSerializer
に存在する理由はわかりません。 ShouldSerialize
は 。Net 1.1 で導入され、IMinOccursバインディングサポートが 。Net 2. で追加されたと信じています。以前の機能は_xsd.exe
_開発チームのニーズ(または好み)を十分に満たしていませんでしたか?
これはプロパティではなくメソッドであるため、_{propertyName}Specified
_パターンの「落とし穴」がありません。また、実際にはより人気があるようで、以下を含む他のシリアライザによって採用されています。
それで、どのパターンを使うのですか?
_xsd.exe
_が_{propertyName}Specified
_プロパティを自動的に生成する場合、または型が特定の要素がXMLファイルに表示されるかどうかを追跡する必要がある場合、または自動生成されたXSDが特定の値であることを示す必要がある場合はオプションです。このパターンを使用して、「落とし穴」に注意してください。
それ以外の場合は、ShouldSerialize{PropertyName}()
パターンを使用します。問題点が少なく、より広くサポートされている可能性があります。
@dbcによる非常に詳細な回答に追加するために、派生クラスでのシリアル化の管理に関する問題に遭遇しました。私の状況では、Prop
プロパティがオーバーライドされた基本クラスと派生クラスがありました。
public class BaseClass
{
public virtual string Prop {get; set;}
}
public class Derived: BaseClass
{
public string Comp1 {get; set;}
public string Comp2 {get; set;}
public override string Prop {get => Comp1 + Comp2; set {}}
}
派生クラスのProp
プロパティが計算されるので、Derived
クラスの場合、Comp1
とComp2
はシリアライズしたかったが、Prop
はシリアライズしたくありませんでした。 XmlIgnore
クラスのProp
プロパティでDerived
属性を設定しても機能せず、Prop
がシリアル化されていることがわかります。
また、ShouldSerializeProp
メソッドとPropSpecified
プロパティをDerived
クラスに追加しようとしましたが、どちらも機能しませんでした。ブレークポイントを設定して、呼び出されているかどうかを確認してみました。
XmlSerializer
は、クラス階層でProp
プロパティが初めて表示される元のクラスを調べて、プロパティをシリアル化するかどうかを決定していることがわかります。派生クラスでシリアル化を制御できるようにするには、最初にBase
クラスにvirtual ShouldSerializeProp
を追加する必要がありました。
public class Base
{
.....
public virtual bool ShouldSerializeProp() {return true;}
}
次に、ShouldSerializeProp
クラスのDerived
をオーバーライドしてfalseを返すことができます。
public class Derived: Base
{
.....
public override bool ShouldSerializeProp() {return false;}
}
このパターンにより、異なる派生クラスは、シリアル化する親クラスからどのプロパティを選択することができます。お役に立てれば。