Sdílet prostřednictvím


Použití třídy XmlSerializer

Windows Communication Foundation (WCF) může pomocí dvou různých technologií serializace převést data v aplikaci na XML, který se přenáší mezi klienty a službami: DataContractSerializer a XmlSerializer.

DataContractSerializer

Ve výchozím nastavení WCF používá DataContractSerializer třídu k serializaci datových typů. Tento serializátor podporuje následující typy:

  • Primitivní typy (například celá čísla, řetězce a bajtová pole) a také některé speciální typy, například XmlElement a DateTime, které jsou považovány za primitivy.

  • Typy kontraktů dat (typy označené atributem DataContractAttribute )

  • Typy označené atributem SerializableAttribute , které zahrnují typy, které implementují ISerializable rozhraní.

  • Typy, které implementují IXmlSerializable rozhraní.

  • Mnoho běžných typů kolekcí, které zahrnují mnoho obecných typů kolekcí.

Mnoho typů rozhraní .NET Framework spadá do těchto dvou kategorií a lze je tedy serializovat. Pole serializovatelných typů jsou také serializovatelná. Úplný seznam najdete v tématu Určení přenosu dat v kontraktech služeb.

Použití DataContractSerializer společně s datovými typy kontraktů je doporučený způsob, jak psát nové služby WCF. Další informace najdete v tématu Použití kontraktů dat.

XmlSerializer

WCF také podporuje XmlSerializer třídu. Třída XmlSerializer není pro WCF jedinečná. Je to stejný serializační modul, který ASP.NET webové služby používají. Třída XmlSerializer podporuje mnohem užší sadu typů než DataContractSerializer třída, ale umožňuje mnohem větší kontrolu nad výsledným XML a podporuje mnohem více standardu XSD (XML Schema Definition Language). Nevyžaduje také žádné deklarativní atributy u serializovatelných typů. Další informace naleznete v tématu serializace XML v dokumentaci rozhraní .NET Framework. Třída XmlSerializer nepodporuje datové typy kontraktů.

Při použití Svcutil.exe nebo funkce Add Service Reference v sadě Visual Studio k vygenerování kódu klienta pro službu třetí strany nebo pro přístup ke schématu třetí strany se pro vás automaticky vybere příslušný serializátor. Pokud schéma není kompatibilní s objektem DataContractSerializer, XmlSerializer je vybráno.

Přepnutí na XmlSerializer

Někdy může být nutné ručně přepnout na tlačítko XmlSerializer. K tomu dochází například v následujících případech:

  • Při migraci aplikace z ASP.NET webových služeb do WCF můžete místo vytváření nových typů kontraktů dat chtít znovu použít existující XmlSerializertypy kompatibilní s daty.

  • Pokud je důležitá přesná kontrola xml, která se zobrazí ve zprávách, ale dokument WSDL (Web Services Description Language) není k dispozici, například při vytváření služby s typy, které musí splňovat určité standardizované publikované schéma, které není kompatibilní s DataContractSerializer.

  • Při vytváření služeb, které následují starší standard kódování SOAP.

V těchto a dalších případech můžete ručně přepnout na XmlSerializer třídu použitím atributu XmlSerializerFormatAttribute pro vaši službu, jak je znázorněno v následujícím kódu.

[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.
<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.

Aspekty zabezpečení

Poznámka:

Při přepínání serializačních motorů je důležité dávat pozor. Stejný typ může serializovat do XML odlišně v závislosti na použitém serializátoru. Pokud omylem použijete nesprávný serializátor, můžete uvést informace z typu, který jste nechtěli zveřejnit.

Například DataContractSerializer třída serializuje pouze členy označené atributem DataMemberAttribute při serializaci datových typů kontraktů. Třída XmlSerializer serializuje libovolný veřejný člen. Podívejte se na typ v následujícím kódu.

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

Pokud se typ neúmyslně použije ve smlouvě o službě, kde je vybrána třída XmlSerializer, pak se člen creditCardNumber serializuje, což pravděpodobně není zamýšleno.

I když DataContractSerializer je třída výchozí, můžete ji explicitně vybrat pro vaši službu (i když by to nikdy nemělo být vyžadováno) použitím DataContractFormatAttribute atributu na typ kontraktu služby.

Serializátor používaný pro službu je nedílnou součástí kontraktu a nelze ho změnit výběrem jiné vazby nebo změnou jiného nastavení konfigurace.

Další důležité aspekty zabezpečení platí pro XmlSerializer třídu. Nejprve důrazně doporučujeme, aby všechny aplikace WCF, které používají XmlSerializer třídu, jsou podepsány klíčem, který je chráněn před zveřejněním. Toto doporučení platí jak v případě, že se provádí ruční přepnutí na XmlSerializer, tak i při automatickém přepnutí (pomocí Svcutil.exe, Přidat odkaz na službu, nebo podobného nástroje). Důvodem je to, že XmlSerializer modul serializace podporuje načítání předgenerovaných serializačních sestavení , pokud jsou podepsány pomocí stejného klíče jako aplikace. Nepodepsaná aplikace je zcela nechráněná před možností škodlivého sestavení odpovídající očekávanému názvu předgenerovaného sestavení serializace, které se umístí do složky aplikace nebo globální mezipaměti sestavení. Útočník musí samozřejmě nejprve získat přístup k zápisu dat do jednoho z těchto dvou umístění, aby se o tuto akci pokusil.

Další hrozba, která existuje při každém použití XmlSerializer , souvisí s přístupem k zápisu do dočasné složky systému. Motor XmlSerializer serializace vytvoří a používá dočasná serializační sestavení v této složce. Měli byste vědět, že jakýkoli proces s přístupem k zápisu do dočasné složky může přepsat tato sestavení serializace škodlivým kódem.

Pravidla pro podporu XmlSerializer

U parametrů operace kontraktu nebo návratových hodnot nelze přímo použít XmlSerializeratributy kompatibilní s kontraktem. Lze je však použít pro typované zprávy (části těla kontraktu zprávy), jak je znázorněno v následujícím kódu.

[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;
}
<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

Při použití na typové členy zprávy tyto atributy přepisují vlastnosti, které kolidují s atributy typové zprávy. Například v následujícím kódu ElementName přepíše Name.

    [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;
}
<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

Atribut MessageHeaderArrayAttribute není podporován při použití .XmlSerializer

Poznámka:

V tomto případě XmlSerializer vyvolá následující výjimku, která je vydána před WCF: "Prvek deklarovaný na nejvyšší úrovni schématu nemůže mít maxOccurs> hodnotu 1. Poskytněte element obálky pro 'více' použitím XmlArray nebo XmlArrayItem místo XmlElementAttribute, nebo použitím stylu parametrů obálky.

Pokud se vám taková výjimka zobrazí, prozkoumejte, jestli se tato situace týká.

WCF nepodporuje SoapIncludeAttribute a XmlIncludeAttribute atributy v kontraktech zpráv a kontraktech operací; místo toho atribut použijte KnownTypeAttribute .

Typy, které implementují rozhraní IXmlSerializable

Typy, které implementují rozhraní IXmlSerializable, jsou plně podporovány DataContractSerializer. Atribut XmlSchemaProviderAttribute by měl být vždy použit u těchto typů pro řízení jejich schématu.

Výstraha

Pokud serializujete polymorfní typy, je nutné použít XmlSchemaProviderAttribute na typ, aby se zajistilo, že správný typ je serializován.

Existují tři odrůdy typů, které implementují IXmlSerializable: typy, které představují libovolný obsah, typy, které představují jeden prvek, a starší DataSet typy.

  • Typy obsahu používají metodu zprostředkovatele schématu určenou atributem XmlSchemaProviderAttribute . Metoda nevrací null a IsAny vlastnost atributu je ponechána ve své výchozí hodnotě false. Toto je nejběžnější použití IXmlSerializable typů.

  • Typy elementů se používají, když typ musí určovat vlastní název kořenového elementu IXmlSerializable. Chcete-li označit typ jako typ elementu, buď nastavte vlastnost IsAny na atributu XmlSchemaProviderAttribute na true nebo vraťte null z metody poskytovatele schématu. Pro typy prvků je volitelná metoda zprostředkovatele schématu – místo názvu metody můžete zadat null v poli XmlSchemaProviderAttribute. Pokud je IsAnytrue a je zadána metoda zprostředkovatele schématu, metoda musí vrátit null.

  • Starší DataSet typy jsou IXmlSerializable typy, které nejsou označené atributem XmlSchemaProviderAttribute . Místo toho spoléhají na metodu GetSchema generování schématu. Tento model se používá pro DataSet typ a jeho typová datová sada odvozuje třídu v dřívějších verzích rozhraní .NET Framework, ale nyní je zastaralá a je podporována pouze ze starších důvodů. Nespoléhejte na tento vzor a vždy použijte XmlSchemaProviderAttribute na své IXmlSerializable typy.

Typy obsahu IXmlSerializable

Při serializaci datového členu typu, který implementuje IXmlSerializable a je typ obsahu definovaný dříve, serializátor zapíše element obálky pro datový člen a předá řízení metodě WriteXml . Implementace WriteXml může napsat libovolný XML, který zahrnuje přidání atributů do elementu obálky. Serializátor zavře prvek po dokončení WriteXml.

Při deserializaci datového členu typu, který implementuje IXmlSerializable a je typ obsahu definovaný dříve, deserializátor umístí čtenář XML do elementu obálky pro datový člen a předá řízení metodě ReadXml . Metoda musí číst celý prvek, včetně počáteční a koncové značky. Ujistěte se, že kód ReadXml zpracovává případ, kdy je element prázdný. Kromě toho by vaše ReadXml implementace neměla spoléhat na element obálky, který je pojmenován určitým způsobem. Název zvolený serializátorem se může lišit.

Je povoleno přiřazovat IXmlSerializable typy obsahu polymorfně, například datovým členům typu Object. Je také povoleno, aby instance typů měly hodnotu null. A konečně je možné použít IXmlSerializable typy s povoleným zachováním objektového grafu a s NetDataContractSerializer. Všechny tyto funkce vyžadují, aby serializátor WCF připojil určité atributy do elementu obálky ("nil" a "type" v oboru názvů instance schématu XML a "Id", "Ref", "Type" a "Assembly" v oboru názvů specifickém pro WCF).

Atributy, které se mají ignorovat při implementaci readXml

Před předáním řízení vašemu kódu ReadXml deserializátor prozkoumá element XML, zjistí tyto speciální atributy XML a provede příslušné kroky. Pokud je například "nil" true, hodnota null je deserializována a ReadXml není volána. Pokud je zjištěn polymorfismus, obsah prvku je deserializován, jako by to byl jiný typ. Implementace typu přiřazeného polymorfně ReadXml je volána. V každém případě by implementace měla tyto speciální atributy ignorovat, ReadXml protože jsou zpracovávány deserializátorem.

Úvahy o schématu pro typy obsahu IXmlSerializable

Při exportu schématu a IXmlSerializable typu obsahu se volá metoda zprostředkovatele schématu. XmlSchemaSet je předán metodě poskytovatele schématu. Metoda může do sady schématu přidat jakékoli platné schéma. Sada schémat obsahuje schéma, které je již známo v době, kdy dojde k exportu schématu. Pokud metoda zprostředkovatele schématu musí přidat položku do sady schématu, musí určit, zda již v sadě existuje položka XmlSchema s odpovídajícím oborem názvů. Pokud ano, metoda zprostředkovatele schématu musí přidat novou položku do existujícího XmlSchema. V opačném případě musí vytvořit novou XmlSchema instanci. To je důležité, pokud se používají pole typů IXmlSerializable . Například, pokud máte typ IXmlSerializable, který se exportuje jako typ "A" v oboru názvů "B", je možné, že v době, kdy je metoda zprostředkovatele schématu volána, sada schémat již obsahuje schéma pro "B" k držení typu "ArrayOfA".

Kromě přidání typů do objektu XmlSchemaSetmusí metoda zprostředkovatele schématu pro typy obsahu vrátit hodnotu, která není null. Může vrátit XmlQualifiedName název typu schématu, který se má pro daný IXmlSerializable typ použít. Tento kvalifikovaný název slouží také jako název datového kontraktu a jmenný prostor pro typ. Je povoleno vrátit typ, který v sadě schématu neexistuje okamžitě, když metoda zprostředkovatele schématu vrátí. Očekává se však, že když jsou všechny související typy exportovány (metoda Export je volána pro všechny relevantní typy na XsdDataContractExporter a vlastnost Schemas je přístupná), typ existuje v sadě schémat. Přístup k vlastnosti Schemas předtím, než byla provedena všechna relevantní volání Export, může vyústit v XmlSchemaException. Další informace o procesu exportu naleznete v tématu Export schémat z tříd.

Metoda zprostředkovatele schématu může také vrátit XmlSchemaType pro použití. Typ může nebo nemusí být anonymní. Pokud je typ anonymní, schéma pro daný IXmlSerializable typ se exportuje jako anonymní typ pokaždé, když je IXmlSerializable typ použit jako datový člen. Typ IXmlSerializable má stále název datového kontraktu a jmenný prostor. (Určuje se podle popisu v názvech kontraktů dat s tím rozdílem, že DataContractAttribute atribut nelze použít k přizpůsobení názvu.) Pokud není anonymní, musí to být jeden z typů v souboru XmlSchemaSet. Tento případ je ekvivalentem vrácení XmlQualifiedName typu.

Kromě toho je globální deklarace elementu exportována pro typ. Pokud typ nemá XmlRootAttribute atribut použitý na něm, element má stejný název a obor názvů jako datový kontrakt a jeho „nillable“ vlastnost je true. Jedinou výjimkou je obor názvů schématu (http://www.w3.org/2001/XMLSchema) – pokud je datový kontrakt typu v tomto oboru názvů, odpovídající globální prvek je v prázdném oboru názvů, protože není zakázáno přidávat nové elementy do oboru názvů schématu. Pokud má typ XmlRootAttribute atribut na něj aplikovaný, globální deklarace elementu je exportována pomocí následujících vlastností: ElementName, Namespace a IsNullable. Výchozí hodnoty použité s XmlRootAttribute jsou název kontraktu dat, prázdný obor názvů a hodnota "nillable" jako true.

Stejná pravidla deklarace globálních elementů platí pro starší typy datových sad. Všimněte si, že XmlRootAttribute nemůže přepsat globální deklarace elementů, které byly přidány prostřednictvím vlastního kódu, a to buď pomocí metody poskytovatele schématu do XmlSchemaSet, nebo prostřednictvím GetSchema pro starší typy datových sad.

IXmlSerializable: typy elementů

IXmlSerializable Typy elementů mají buď vlastnost nastavenou na IsAny, nebo jejich metoda poskytovatele schématu vrátí true.

Serializace a deserializace typu prvku je velmi podobná serializaci a deserializaci typu obsahu. Existují však některé důležité rozdíly:

  • Očekává se WriteXml , že implementace zapíše přesně jeden prvek (což může samozřejmě obsahovat více podřízených prvků). Neměly by být psány atributy mimo tento jediný prvek, více sourozeneckých prvků nebo smíšeného obsahu. Prvek může být prázdný.

  • Implementace ReadXml by neměla číst element obálky. Očekává se, že přečte jeden prvek, který WriteXml vytvoří.

  • Při pravidelné serializaci prvkového typu (například jako datový člen v datovém kontraktu) serializátor vytvoří obalový element před voláním WriteXml, podobně jako u obsahových typů. Při serializaci typu elementu na nejvyšší úrovni serializátor obvykle nevypisuje žádný obalový element kolem elementu, který WriteXml zapisuje, pokud při vytváření serializátoru v konstruktoru DataContractSerializer nebo NetDataContractSerializer není explicitně zadán kořenový název a obor názvů. Další informace naleznete v tématu Serializace a deserializace.

  • Při serializaci typu prvku na nejvyšší úrovni bez zadání kořenového názvu a oboru názvů v době výstavby, WriteStartObject a WriteEndObject v podstatě nedělají nic a WriteObjectContent volá WriteXml. V tomto režimu objekt nemůže být null a nelze ho přiřadit polymorfně. Nelze také povolit zachování grafu objektů a nelze použít NetDataContractSerializer.

  • Při deserializaci typu prvku na nejvyšší úrovni bez zadání kořenového názvu a oboru názvů v době konstrukce, vrátí IsStartObjecttrue, pokud může najít začátek libovolného prvku. ReadObject s parametrem verifyObjectName nastaveným na true se chová stejným způsobem jako IsStartObject před skutečným načtením objektu. ReadObject pak předá řízení metodě ReadXml .

Schéma exportované pro typy prvků je stejné jako pro XmlElement typ, jak je popsáno v předchozí části, s tím rozdílem, že metoda zprostředkovatele schématu může přidat jakékoli další schéma jako XmlSchemaSet u typů obsahu. Použití atributu XmlRootAttribute s typy elementů není povoleno a globální deklarace elementů se pro tyto typy nikdy nevygenerují.

Rozdíly od XmlSerializeru

Rozhraní IXmlSerializable a atributy XmlSchemaProviderAttribute a XmlRootAttribute rozumí i XmlSerializer. Existují však určité rozdíly v tom, jak se s nimi v modelu kontraktu dat zachází. Důležité rozdíly jsou shrnuty v následujícím seznamu:

  • Metoda zprostředkovatele schématu musí být veřejná, aby se používala v XmlSerializermodelu kontraktu dat, ale nemusí být veřejná.

  • Metoda poskytovatele schématu je volána, když IsAny je true v datovém kontraktu, ale ne s XmlSerializer.

  • XmlRootAttribute Pokud atribut není k dispozici pro obsah nebo starší typy datových sad, XmlSerializer exportuje globální deklaraci elementu do prázdného oboru názvů. V datovém modelu kontraktu se obvykle používá obor názvů datového kontraktu, což je popsáno výše.

Mějte na paměti tyto rozdíly při vytváření typů, které se používají s oběma technologiemi serializace.

Importování schématu IXmlSerializable

Při importu schématu generovaného z IXmlSerializable typů existuje několik možností:

  • Vygenerované schéma může být platné schéma kontraktu dat, jak je popsáno v referenčních informacích ke schématu kontraktu dat. V tomto případě lze schéma importovat obvyklým způsobem a vygenerují se běžné typy datových kontraktů.

  • Vygenerované schéma nemusí být platným schématem kontraktu dat. Například vaše metoda zprostředkovatele schématu může generovat schéma, které zahrnuje atributy XML, které nejsou podporovány v modelu kontraktu dat. V tomto případě můžete schéma importovat jako IXmlSerializable typy. Tento režim importu není ve výchozím nastavení zapnutý, ale dá se snadno povolit – například s přepínačem příkazového /importXmlTypes řádku na nástroj ServiceModel Metadata Utility (Svcutil.exe). Toto je podrobně popsáno v importu schématu pro generování tříd. Všimněte si, že musíte pracovat přímo s XML pro instance typu. Můžete také zvážit použití jiné serializace technologie, která podporuje širší rozsah schématu – viz téma o použití XmlSerializer.

  • Možná budete chtít znovu použít existující IXmlSerializable typy v proxy serveru místo generování nových typů. V tomto případě lze funkci odkazovaných typů popsanou v tématu Importing Schema to Generate Types použít k označení typu, který se má použít. To odpovídá použití /reference přepínače svcutil.exe, který určuje sestavení, které obsahuje typy pro opakované použití.

Chování z předchozí verze XmlSerializeru

V rozhraní .NET Framework 4.0 a starších XmlSerializer vytvářel dočasná sestavení pro serializaci zápisem kódu jazyka C# do souboru. Soubor byl pak zkompilován do sestavení. Toto chování mělo několik nežádoucích důsledků, jako je zpomalení doby spuštění serializátoru. V rozhraní .NET Framework 4.5 se toto chování změnilo tak, aby vygenerovalo sestavení bez nutnosti použití kompilátoru. Někteří vývojáři můžou chtít vidět vygenerovaný kód jazyka C#. Toto starší chování můžete zadat pomocí následující konfigurace:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.xml.serialization>
    <xmlSerializer tempFilesLocation='e:\temp\XmlSerializerBug' useLegacySerializerGeneration="true" />
  </system.xml.serialization>
  <system.diagnostics>
    <switches>
      <add name="XmlSerialization.Compilation" value="1" />
    </switches>
  </system.diagnostics>
</configuration>

Pokud narazíte na problémy s kompatibilitou, jako XmlSerializer je selhání serializace odvozené třídy s neveřejným novým přepsáním, můžete přepnout zpět na XMLSerializer starší verzi chování pomocí následující konfigurace:

<configuration>
  <appSettings>
    <add key="System:Xml:Serialization:UseLegacySerializerGeneration" value="true" />
  </appSettings>
</configuration>

Jako alternativu k předchozí konfiguraci můžete použít následující konfiguraci na počítači s rozhraním .NET Framework 4.5 nebo novější verzí:

<configuration>
  <system.xml.serialization>
    <xmlSerializer useLegacySerializerGeneration="true"/>
  </system.xml.serialization>
</configuration>

Poznámka:

Přepínač <xmlSerializer useLegacySerializerGeneration="true"/> funguje jenom na počítači s rozhraním .NET Framework 4.5 nebo novější verzí. Výše uvedený appSettings přístup funguje ve všech verzích rozhraní .NET Framework.

Viz také