XmlSerializer クラスの使用

Windows Communication Foundation (WCF) では、各種のシリアル化テクノロジを使用して、アプリケーション データをクライアントとサービス間で転送される XML に変換できます。この処理をシリアル化といいます。

既定としての DataContractSerializer

WCF の既定では、データ型をシリアル化するのに DataContractSerializer クラスが使用されます。このシリアライザーは、次の型をサポートします。

  • プリミティブ型 (整数、文字列、バイト配列など) や、プリミティブとして処理される XmlElementDateTime などの特殊な型。

  • データ コントラクト型 (DataContractAttribute 属性でマークされた型)。

  • ISerializable インターフェイスを実装する型など、SerializableAttribute 属性でマークされた型。

  • IXmlSerializable インターフェイスを実装する型。

  • 多くのジェネリック コレクション型を含む多くの共通コレクション型。

多くの .NET Framework 型は、上に記載した一覧のうち、下 2 つのカテゴリに分類され、したがってシリアル化可能です。シリアル化可能な型の配列もシリアル化可能です。完全な一覧については、「サービス コントラクトでのデータ転送の指定」を参照してください。

新しい WCF サービスを記述する方法としては、データ コントラクト型と共に使用される DataContractSerializer が推奨されます。詳細については、次のトピックを参照してください。 データ コントラクトの使用.

XmlSerializer クラスを使用する場合

WCF は XmlSerializer クラスもサポートします。XmlSerializer クラスは、WCF 独自のものではありません。これは、ASP.NET Web サービスが使用するのと同じシリアル化エンジンです。XmlSerializer クラスでは、DataContractSerializer クラスよりもサポートされる型の範囲がずっと狭くなりますが、結果の XML に対する制御の柔軟性に優れています。また、XML スキーマ定義言語 (XSD) 標準のサポート範囲が広く、シリアル化可能な型で宣言型属性が要求されません。詳細については、次のトピックを参照してください。 .NET Framework のドキュメントの XML シリアル化に関するトピックを参照してください。XmlSerializer クラスは、データ コントラクト型をサポートしません。

Visual Studio の Svcutil.exe または [サービス参照の追加] 機能を使用して、サードパーティ サービス用のクライアント コードを生成したり、サードパーティ スキーマを利用する場合、適切なシリアライザーが自動的に選択されます。スキーマに DataContractSerializer との互換性がない場合は、XmlSerializer が選択されます。

XmlSerializer への手動切り替え

XmlSerializer に手動で切り替える必要が生じる場合もあります。たとえば、次のような場合です。

  • アプリケーションを ASP.NET Web サービスから WCF に移行するときに、新しいデータ コントラクト型を作成するのではなく、既存の XmlSerializer 互換の型を再利用する場合。

  • メッセージに表示する XML に対する正確な制御が必要で、Web サービス記述言語 (WSDL) ドキュメントが利用できない場合。たとえば、DataContractSerializer と互換性がなく、標準化および公開されている特定のスキーマに従う必要のある型を使用して、サービスを作成する場合。

  • 従来の SOAP エンコード標準に従うサービスを作成する場合。

上記を含めた多様な状況で、次のコードに示すように、XmlSerializerFormatAttribute 属性をサービスに適用することにより、XmlSerializer クラスに手動で切り替えることができます。

<ServiceContract(), XmlSerializerFormat()>  _
Public Class BankingService
    <OperationContract()> _
    Public Sub ProcessTransaction(ByVal bt As BankingTransaction) 
       ' Code not shown.
    End Sub 
End Class 


' BankingTransaction is not a data contract class,
' but is an XmlSerializer-compatible class instead.

Public Class BankingTransaction
    <XmlAttribute()>  _
    Public Operation As String
    <XmlElement()>  _
    Public fromAccount As Account
    <XmlElement()>  _
    Public toAccount As Account
    <XmlElement()>  _
    Public amount As Integer
End Class
'Notice that the Account class must also be XmlSerializer-compatible.
[ServiceContract]
[XmlSerializerFormat]
public class BankingService
{
[OperationContract]
    public void ProcessTransaction(BankingTransaction bt)
    {
        // Code not shown.
    }
    
}

//BankingTransaction is not a data contract class,
//but is an XmlSerializer-compatible class instead.
public class BankingTransaction
{
    [XmlAttribute]
    public string Operation;
    [XmlElement]
    public Account fromAccount;
    [XmlElement]
    public Account toAccount;
    [XmlElement]
    public int amount;
}
//Notice that the Account class must also be XmlSerializer-compatible.

セキュリティに関する注意事項

ms733901.note(ja-jp,VS.100).gif注 :
シリアル化エンジンを切り替える場合は注意が必要です。同じ型でも、使用するシリアライザーによって XML へのシリアル化方法が異なる場合があります。誤って、不適切なシリアライザーを使用すると、公開する意図のない型の情報が公開されるおそれがあります。

たとえば、DataContractSerializer クラスは、データ コントラクト型をシリアル化する場合、DataMemberAttribute 属性でマークされたメンバーのみをシリアル化します。XmlSerializer クラスは、すべてのパブリック メンバーをシリアル化します。たとえば、次のコードの型を見てください。

<DataContract()>  _
Public Class Customer
    <DataMember()>  _
    Public firstName As String
    <DataMember()>  _
    Public lastName As String
    Public creditCardNumber As String
End Class 
[DataContract]
public class Customer
{
    [DataMember]
    public string firstName;
    [DataMember]
    public string lastName;
    public string creditCardNumber;
}

XmlSerializer クラスが選択されているサービス コントラクトでこの型が誤って使用された場合、意図したものではなくても creditCardNumber メンバーがシリアル化されます。

DataContractSerializer クラスが既定の場合でも、DataContractFormatAttribute 属性をサービス コントラクト型に適用することで、このクラスをサービスに対して明示的に選択できます (ただし、この操作が必要になることはありません)。

サービスで使用するシリアライザーはコントラクトにとって不可欠な部分です。別のバインディングを選択しても他の構成設定に変更しても、このシリアライザーを変更することはできません。

XmlSerializer クラスについては、セキュリティに関する重要な考慮事項が他にもあります。まず、XmlSerializer クラスを使用するすべての WCF アプリケーションには、情報の漏えいから保護されたキーを使って署名することを強くお勧めします。このことは、XmlSerializer に手動で切り替える場合と、自動切り替えが行われる場合 (Svcutil.exe やサービス参照の追加などのツールによる) の両方でお勧めします。これは、XmlSerializer シリアル化エンジンで事前生成済みのシリアル化アセンブリの読み込みがサポートされており、これらがアプリケーションと同じキーで署名されるためです。署名されていないアプリケーションは、アプリケーション フォルダーやグローバル アセンブリ キャッシュに配置される事前生成済みのシリアル化アセンブリで予想される名前に、悪意のあるアセンブリが一致するという危険性に対して完全に無防備になります。もちろん、攻撃者がこのようなアクションを行うには、この 2 つの場所のいずれかに対する書き込みアクセスをまず取得する必要があります。

XmlSerializer を使用するときに必ず存在するもう 1 つの脅威は、システムの一時フォルダーへの書き込みアクセスに関連するものです。XmlSerializer シリアル化エンジンは、このフォルダー内に一時的なシリアル化アセンブリを作成して使用します。一時フォルダーへの書き込みアクセスのあるプロセスであれば、悪意のあるコードによってこれらのシリアル化アセンブリが上書きされるおそれがあることに注意してください。

XmlSerializer サポートのルール

XmlSerializer 互換の属性は、コントラクト操作のパラメーターまたは戻り値に直接適用できません。ただし、次のコードに示すように、型指定されたメッセージ (メッセージ コントラクトの本文) には適用できます。

<ServiceContract(), XmlSerializerFormat()>  _
Public Class BankingService
    <OperationContract()> _
    Public Sub ProcessTransaction(ByVal bt As BankingTransaction) 
       'Code not shown.
    End Sub 
End Class 

<MessageContract()>  _
Public Class BankingTransaction
    <MessageHeader()>  _
    Public Operation As String
    <XmlElement(), MessageBodyMember()>  _
    Public fromAccount As Account
    <XmlElement(), MessageBodyMember()>  _
    Public toAccount As Account
    <XmlAttribute(), MessageBodyMember()>  _
    Public amount As Integer
End Class 
[ServiceContract]
[XmlSerializerFormat]
public class BankingService
{
    [OperationContract]
    public void ProcessTransaction(BankingTransaction bt)
    {
        //Code not shown.
    }
}

[MessageContract]
public class BankingTransaction
{
    [MessageHeader]
    public string Operation;
    [XmlElement, MessageBodyMember]
    public Account fromAccount;
    [XmlElement, MessageBodyMember]
    public Account toAccount;
    [XmlAttribute, MessageBodyMember]
    public int amount;
}

型指定されたメッセージのメンバーに適用する場合、型指定されたメッセージ属性で競合するプロパティは、この属性によりオーバーライドされます。たとえば、次のコードの ElementName は、Name をオーバーライドします。

<MessageContract()>  _
Public Class BankingTransaction
    <MessageHeader()>  _
    Public Operation As String
    
    'This element will be <fromAcct> and not <from>:
    <XmlElement(ElementName := "fromAcct"), _
        MessageBodyMember(Name := "from")>  _
    Public fromAccount As Account
    
    <XmlElement(), MessageBodyMember()>  _
    Public toAccount As Account
    
    <XmlAttribute(), MessageBodyMember()>  _
    Public amount As Integer
End Class 
    [MessageContract]
    public class BankingTransaction
    {
        [MessageHeader] public string Operation;

        //This element will be <fromAcct> and not <from>:
        [XmlElement(ElementName="fromAcct"), MessageBodyMember(Name="from")] 
        public Account fromAccount;
        
        [XmlElement, MessageBodyMember] 
        public Account toAccount;
        
        [XmlAttribute, MessageBodyMember] 
        public int amount;
}

XmlSerializer の使用時は、MessageHeaderArrayAttribute 属性はサポートされません。

ms733901.note(ja-jp,VS.100).gif注 :
この場合、WCF より前にリリースされた XmlSerializer によって、"スキーマのトップ レベルで宣言された要素に maxOccurs > 1 の値を含めることはできません。XmlElementAttribute ではなく、XmlArray または XmlArrayItem を使うか、Wrapped パラメーター スタイルを使って 'more' のラッパー要素を指定してください。" という例外がスローされます。

この例外が出力された場合は、この状況が当てはまるかどうかを調査します。

WCF では、メッセージ コントラクトおよび操作コントラクトでの SoapIncludeAttribute 属性および XmlIncludeAttribute 属性はサポートされていません。代わりに、KnownTypeAttribute 属性を使用してください。

IXmlSerializable インターフェイスを実装する型

IXmlSerializable インターフェイスを実装する型は、DataContractSerializer で完全にサポートされます。これらの型には、スキーマを制御するために必ず XmlSchemaProviderAttribute 属性を適用する必要があります。

ms733901.Warning(ja-jp,VS.100).gif 注意 :
ポリモーフィック型をシリアル化する場合、XmlSchemaProviderAttribute を型に適用して、正しい型がシリアル化されるようにする必要があります。

IXmlSerializable を実装する型には、任意のコンテンツを表す型、1 つの要素を表す型、および従来の DataSet 型の 3 種類があります。

  • コンテンツ型では、XmlSchemaProviderAttribute 属性によって指定されたスキーマ プロバイダー メソッドが使用されます。このメソッドから null が返されることはなく、属性の IsAny プロパティは既定値 false のままになります。これは、IXmlSerializable 型の最も一般的な使用方法です。

  • 要素型は、IXmlSerializable 型が自身のルート要素名を制御する必要があるときに使用します。型を要素型としてマークするには、XmlSchemaProviderAttribute 属性の IsAny プロパティを true に設定するか、スキーマ プロバイダー メソッドから null を返します。スキーマ プロバイダー メソッドの使用は、要素型ではオプションです。XmlSchemaProviderAttribute でメソッド名の代わりに null を指定できます。ただし、IsAnytrue で、スキーマ プロバイダー メソッドが指定されている場合、メソッドは null を返す必要があります。

  • 従来の DataSet 型は、XmlSchemaProviderAttribute 属性でマークされていない IXmlSerializable 型です。これらの型は、スキーマ生成に関して GetSchema メソッドに依存しています。以前のバージョンの .NET Framework では、このパターンが DataSet 型に使用され、型指定されたデータセットからクラスが派生されます。ただし、現在、このパターンは使用されなくなっており、従来の型に対応することだけを目的としてサポートされています。このパターンに依存せず、必ず XmlSchemaProviderAttributeIXmlSerializable 型に適用してください。

IXmlSerializable コンテンツ型

IXmlSerializable を実装しており、以前に定義したコンテンツ型である型のデータ メンバーをシリアル化すると、シリアライザーはそのデータ メンバーのラッパー要素を書き込み、WriteXml メソッドに制御を渡します。WriteXml 実装により、ラッパー要素に属性が追加されるなど、任意の XML が書き込まれることがあります。WriteXml の実行後、シリアライザーは要素を閉じます。

IXmlSerializable を実装しており、以前に定義したコンテンツ型である型のデータ メンバーを逆シリアル化すると、デシリアライザーはそのデータ メンバーのラッパー要素に XML リーダーを配置し、ReadXml メソッドに制御を渡します。このメソッドは、開始タグと終了タグを含む、要素全体を読み取る必要があります。ReadXml コードでは、要素が空の場合も忘れずに処理してください。また、ReadXml の実装では、特定の方法で名前が付けられたラッパー要素に依存しないようにしてください。シリアライザーによって選択される名前は、異なる場合があります。

IXmlSerializable コンテンツ型は、たとえば Object 型のデータ メンバーなどに、ポリモーフィックに割り当てることができます。また、型インスタンスを null にすることもできます。さらに、IXmlSerializable 型は、オブジェクト グラフの保存を有効にして NetDataContractSerializer で使用することも可能です。これらのすべての機能を実現するには、WCF シリアライザーが特定の属性 (XML スキーマ インスタンス名前空間の "nil" と "type"、WCF 固有の名前空間の "Id"、"Ref"、"Type"、および "Assembly") をラッパー要素に追加する必要があります。

ReadXml を実装するときに無視する属性

ReadXml コードに制御を渡す前に、デシリアライザーは、XML 要素を調べ、前述の特別な XML 属性を検出し、その属性に従って動作します。たとえば、"nil" が true の場合は、null 値が逆シリアル化され、ReadXml は呼び出されません。ポリモーフィズムが検出された場合は、要素のコンテンツが別の型と同様に逆シリアル化されます。ポリモーフィックに割り当てられた型の ReadXml 実装が呼び出されます。どの場合も、これらの特別な属性がデシリアライザーによって処理されるため、ReadXml 実装ではこれらの属性を無視する必要があります。

IXmlSerializable コンテンツ型のスキーマに関する考慮事項

スキーマと IXmlSerializable コンテンツ型をエクスポートすると、スキーマ プロバイダー メソッドが呼び出されます。このスキーマ プロバイダー メソッドには、XmlSchemaSet が渡されます。このメソッドは、有効なスキーマをスキーマ セットに追加できます。スキーマ セットには、スキーマをエクスポートした時点で既に認識されていたスキーマが格納されます。スキーマ プロバイダー メソッドは、スキーマ セットに項目を追加する必要があるときに、適切な名前空間を持つ XmlSchema がそのセットに既に存在するかどうかを確認する必要があります。存在する場合、スキーマ プロバイダー メソッドは新しい項目を既存の XmlSchema に追加する必要があります。存在しない場合、新しい XmlSchema インスタンスを作成する必要があります。これは、IXmlSerializable 型の配列を使用する場合に重要です。たとえば、IXmlSerializable 型を名前空間 "B" の "A" 型としてエクスポートする場合、スキーマ プロバイダー メソッドが呼び出される前に、"B" が "ArrayOfA" 型を保持するためのスキーマがスキーマ セットに既に存在している可能性があります。

型を XmlSchemaSet に追加する以外に、コンテンツ型のスキーマ プロバイダー メソッドは null 以外の値を返す必要があります。このメソッドは、特定の IXmlSerializable 型で使用するスキーマ型の名前を指定する XmlQualifiedName を返すことができます。この修飾名は、その型のデータ コントラクト名および名前空間としても使用されます。スキーマ プロバイダー メソッドは、スキーマ セットにまだ存在していない型であっても、復帰時に返すことができます。ただし、関連するすべての型がエクスポートされる (XsdDataContractExporter の関連するすべての型に対して Export メソッドが呼び出され、Schemas プロパティにアクセスする) までに、その型がスキーマ セットに存在している必要があります。関連するすべての Export 呼び出しが実行される前に Schemas プロパティにアクセスすると、XmlSchemaException が発生する可能性があります。エクスポート プロセス詳細情報、「クラスからのスキーマのエクスポート」を参照してください。

スキーマ プロバイダー メソッドは、使用する XmlSchemaType を返すこともできます。その型は、匿名の場合とそうでない場合があります。匿名の場合、IXmlSerializable 型のスキーマは、IXmlSerializable 型がデータ メンバーとして使用されるたびに匿名型としてエクスポートされます。IXmlSerializable 型には、データ コントラクト名と名前空間が引き続き保持されます (決定方法は、「データ コントラクト名」で説明されているとおりです。ただし、DataContractAttribute 属性を使用して名前をカスタマイズすることはできません)。匿名でない場合、型は XmlSchemaSet に含まれている型のいずれかである必要があります。これは、型の XmlQualifiedName を返す場合と同じです。

さらに、型のグローバル要素宣言がエクスポートされます。型に XmlRootAttribute 属性が適用されていない場合、その要素にはデータ コントラクトと同じ名前と名前空間が使用され、その "nillable" プロパティは true に設定されます。ただし、スキーマ名前空間 ("http://www.w3.org/2001/XMLSchema") だけは例外です。型のデータ コントラクトがこの名前空間にある場合は、スキーマ名前空間に新しい要素を追加することが禁止されているため、対応するグローバル要素は、空白の名前空間に属することになります。型に XmlRootAttribute 属性が適用されている場合、グローバル要素宣言は、ElementNameNamespace、および IsNullable の各プロパティを使用してエクスポートされます。XmlRootAttribute が適用された場合の既定値は、データ コントラクト名、空白の名前空間、および true に設定された "nillable" です。

同じグローバル要素宣言の規則が、従来のデータセット型に適用されます。XmlRootAttribute は、カスタム コードによって追加されたグローバル要素宣言をオーバーライドできません。これには、スキーマ プロバイダー メソッドを使用して XmlSchemaSet に追加された場合と、従来のデータセット型に対して GetSchema を使用して追加された場合があります。

IXmlSerializable 要素型

IXmlSerializable 要素型には、true に設定された IsAny プロパティか、null を返すスキーマ プロバイダー メソッドのいずれかが含まれています。

要素型のシリアル化と逆シリアル化は、コンテンツ型のシリアル化と逆シリアル化に非常に似ています。ただし、重要な違いがいくつかあります。

  • WriteXml の実装では、要素 (これには複数の子要素が含まれている可能性もありますが) を 1 つだけ出力することが想定されています。この 1 つの要素の外側にある属性、複数の兄弟要素、またはこれらが混在したコンテンツを出力することはできません。要素は空であってもかまいません。

  • ReadXml の実装では、ラッパー要素の読み取りは想定されていません。読み取ることが想定されているのは、WriteXml で生成される要素 1 つのみです。

  • 要素型を一様にシリアル化する場合 (データ コントラクトのデータ メンバーとしてシリアル化する場合など) は、コンテンツ型の場合と同様に、WriteXml を呼び出す前にラッパー要素が出力されます。ただし、DataContractSerializer コンストラクターまたは NetDataContractSerializer コンストラクターによるシリアライザーの構築時にルート名と名前空間を明示的に指定しない限り、トップ レベルで要素型をシリアル化しても、通常は WriteXml で書き出される要素を囲むラッパー要素が出力されることはありません。詳細については、次のトピックを参照してください。 シリアル化と逆シリアル化.

  • 構築時にルート名と名前空間を指定せずにトップ レベルで要素型をシリアル化した場合、WriteStartObjectWriteEndObject では基本的に何も実行されず、WriteObjectContent によって WriteXml が呼び出されます。このモードでは、シリアル化されるオブジェクトは null にできず、ポリモーフィックに割り当てることができません。また、オブジェクト グラフの保存を有効化できず、NetDataContractSerializer も使用できません。

  • 構築時にルート名と名前空間を指定せずにトップ レベルで要素型を逆シリアル化したときに、要素の先頭を検出できた場合は、IsStartObjecttrue を返します。verifyObjectName パラメーターが true に設定されている場合に、ReadObject が実際にオブジェクトを読み取る前の動作は IsStartObject と同様です。その後、ReadObject は制御を ReadXml メソッドに渡します。

要素型の場合も、エクスポートされるスキーマは、前のセクションで説明した XmlElement 型に対するスキーマと同じです。ただし、スキーマ プロバイダー メソッドは、コンテンツ型と同様、追加のスキーマを XmlSchemaSet に追加できます。要素型には XmlRootAttribute 属性を使用することはできないので、グローバル要素宣言は要素型に対して出力されません。

XmlSerializer との相違点

IXmlSerializable インターフェイス、XmlSchemaProviderAttribute 属性、および XmlRootAttribute 属性は、XmlSerializer でも認識されます。ただし、データ コントラクト モデルでの処理方法に違いがあります。重要な違いを以下にまとめます。

  • スキーマ プロバイダー メソッドは、XmlSerializer で使用できるようにするためにパブリックにする必要がありますが、データ コントラクト モデルで使用するためにパブリックにする必要はありません。

  • スキーマ プロバイダー メソッドは、データ コントラクト モデルで IsAnytrue の場合に呼び出されますが、XmlSerializer では呼び出されません。

  • コンテンツ型または従来のデータセット型に XmlRootAttribute 属性がない場合、XmlSerializer は、グローバル要素宣言を空白の名前空間にエクスポートします。データ コントラクト モデルで通常使用される名前空間は、前に説明したとおりデータ コントラクトの名前空間です。

両方のシリアル化技術で使用する型を作成する場合には、これらの違いに注意してください。

IXmlSerializable スキーマのインポート

IXmlSerializable 型から生成されたスキーマをインポートする場合、次のような状況が考えられます。

  • 生成されたスキーマが、「データ コントラクト スキーマの参照」に記載された有効なデータ コントラクト スキーマである場合があります。この場合は、スキーマを通常どおりにインポートでき、通常のデータ コントラクト型が生成されます。

  • 生成されたスキーマが、有効なデータ コントラクト スキーマではない場合があります。たとえば、スキーマ プロバイダー メソッドによって、データ コントラクト モデルでサポートされていない XML 属性を含むスキーマが生成されることがあります。この場合、スキーマを IXmlSerializable 型としてインポートできます。このインポート モードは、既定では有効化されていませんが、たとえば、ServiceModel メタデータ ユーティリティ ツール (Svcutil.exe)/importXmlTypes コマンド ライン スイッチを使用することによって、簡単に有効化できます。詳細については、「クラスを作成するためのスキーマのインポート」を参照してください。型インスタンスを処理するには、XML を直接操作する必要があります。XmlSerializer の使用方法に関するトピックを参照し、より広い範囲のスキーマをサポートする別のシリアル化技術を使用することを検討することもできます。

  • 新しい型を生成する代わりに、プロキシ内の既存の IXmlSerializable 型を再利用できます。この場合、「型を作成するためのスキーマのインポート」で説明する参照型の機能を使用して、再利用する型を示すことができます。これは、Svcutil.exe で /reference スイッチを使用して、再利用する型を含むアセンブリを指定することに相当します。

参照

処理手順

方法 : XmlSerializer を使用する WCF クライアント アプリケーションの起動時間を短縮する

リファレンス

DataContractFormatAttribute
DataContractSerializer
XmlSerializer
MessageHeaderArrayAttribute

概念

サービス コントラクトでのデータ転送の指定
データ コントラクトの使用