web-dev-qa-db-ja.com

ShouldSerialize *()と*指定された条件付きシリアル化パターン

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);
}
22
JNYRanger

_{propertyName}Specified_パターンの意図は XMLスキーマバインディングサポート:MinOccurs属性バインディングサポート に記載されています。次のようなXSDスキーマ要素をサポートするために追加されました。

  • _<element>_要素が関係しています。
  • minOccursはゼロです。
  • maxOccurs属性は、単一のインスタンスを指示します。
  • データ型は値型に変換されます。

この場合、 _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>_にバインドしないのはなぜですか?おそらく:

  • Nullableは、代わりに _xsi:nil="true"_ 属性をサポートするために使用されます。 Xsi:nil Attribute Binding Support を参照してください。
  • Nullableは.Net 2.0まで導入されなかったので、この目的でNullableを使用するには遅すぎるのでしょうか?

_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_パターンの「落とし穴」がありません。また、実際にはより人気があるようで、以下を含む他のシリアライザによって採用されています。

それで、どのパターンを使うのですか?

  1. _xsd.exe_が_{propertyName}Specified_プロパティを自動的に生成する場合、または型が特定の要素がXMLファイルに表示されるかどうかを追跡する必要がある場合、または自動生成されたXSDが特定の値であることを示す必要がある場合はオプションです。このパターンを使用して、「落とし穴」に注意してください。

  2. それ以外の場合は、ShouldSerialize{PropertyName}()パターンを使用します。問題点が少なく、より広くサポートされている可能性があります。

35
dbc

@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クラスの場合、Comp1Comp2はシリアライズしたかったが、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;}
}

このパターンにより、異なる派生クラスは、シリアル化する親クラスからどのプロパティを選択することができます。お役に立てれば。

6
Tibi