XML-Serialisierung im .NET Framework

 

Dare Obasanjo
Microsoft Corporation

23. Januar 2003

Zusammenfassung: Dare Obasanjo erläutert, wie Sie mithilfe der XML-Serialisierung stark typisierte XML innerhalb der .NET Framework verarbeiten können, während W3C-Standards unterstützt und die Interoperabilität verbessert wird. In diesem Artikel ist auch eine häufig gestellte Frage enthalten. (11 gedruckte Seiten)

Laden Sie die xml01202003_sample.exeherunter .

Die bisherige Geschichte

In meinen vorherigen Spalten Dinge zu wissen und vermeiden beim Abfragen von XML-Dokumenten mit XPath und Arbeiten mit Namespaces im XML-Schema erwähnte ich das Erstellen eines XML-Formats zum Nachverfolgen der Bücher in meiner persönlichen Bibliothek. Während der Arbeit mit diesem Format habe ich verschiedene Aspekte von W3C-Empfehlungen untersucht, z. B. XPath und XML-Schema. Im Artikel dieses Monats werde ich die Verwendung der XML-Serialisierungstechnologie im .NET Framework mit meinem XML-Format untersuchen und einige der häufig gestellten Fragen zur .NET Framework-basierten XML-Serialisierung beantworten. Komm mit, es sollte eine interessante Fahrt sein.

Übersicht über die XML-Serialisierung im .NET Framework

Der Hauptzweck der XML-Serialisierung im .NET Framework besteht darin, die Konvertierung von XML-Dokumenten und -Streams in Common Language Runtime-Objekte und umgekehrt zu ermöglichen. Die Serialisierung von XML in Common Language Runtime-Objekte ermöglicht die Konvertierung von XML-Dokumenten in eine Form, in der sie mit herkömmlichen Programmiersprachen einfacher verarbeitet werden können. Andererseits erleichtert die Serialisierung von Objekten in XML das Beibehalten oder Transportieren des Zustands solcher Objekte auf offene, standardskonforme und plattformunabhängige Weise.

Die XML-Serialisierung im .NET Framework unterstützt die Serialisierung von Objekten entweder als XML, das einem angegebenen XSD-Schema (W3C XML Schema Definition) entspricht oder dem in Abschnitt fünf der SOAP-Spezifikation definierten Serialisierungsformat entspricht. Während der XML-Serialisierung werden nur die öffentlichen Eigenschaften und Felder eines Objekts serialisiert. Außerdem wird die Typtreue während der XML-Serialisierung nicht immer beibehalten. Wenn Sie also für instance über ein Book-Objekt verfügen, das im Library-Namespace vorhanden ist, gibt es keine Garantie, dass es in ein Objekt deserialisiert wird, das denselben Typ aufweist. Dies bedeutet jedoch, dass objekte, die mithilfe der XML-Serialisierung im .NET Framework serialisiert wurden, von einem Computer an den anderen gesendet werden können, ohne dass der ursprüngliche Typ auf dem Zielcomputer vorhanden sein muss oder dass der XML-Code sogar mit dem .NET Framework verarbeitet wird. Die XML-Serialisierung von Objekten ist ein nützlicher Mechanismus für diejenigen, die Daten mithilfe plattformunabhängiger Technologien wie XML und SOAP bereitstellen oder nutzen möchten.

XML-Dokumente, die vom XML-Serialisierungsprozess in Objekte konvertiert werden, sind stark typisiert. Datentypinformationen werden den Elementen und Attributen in einem XML-Dokument über ein Schema zugeordnet, das in der W3C-XSD-Sprache (XML Schema Definition) geschrieben ist. Die Datentypinformationen im Schema ermöglichen es xmlSerializer , XML-Dokumente in stark typisierte Klassen zu konvertieren.

Weitere Informationen zur XML-Serialisierung im .NET Framework finden Sie im SDK-Dokumentationsthema XML- und SOAP-Serialisierung.

Die Buchinventuranwendung

In meinen vorherigen Artikeln habe ich ein XML-Dokument erstellt, in dem alle meine Bücher aufgelistet sind und deren Verfügbarkeit in meiner persönlichen Bibliothek beschrieben wurde. Nach der Überlegung entschied ich, dass ich eine GUI-Schnittstelle zum Anzeigen und Bearbeiten des Dokuments bevorzugen würde, anstatt die XML-Rohdatei in einem Text-Editor zu bearbeiten. Der erste Schritt, den ich beim Erstellen dieser Anwendung unternommen habe, bestand darin, die Klassen im System.Windows.Forms-Namespace zu untersuchen, um festzustellen, ob eine meiner Anforderungen sofort erfüllt werden konnte. Die DataGrid-Klasse sah vielversprechend aus.

Die Beschreibung potenzieller Datenquellen für die DataGrid-Klasse umfasste eindimensionale Arrays, was einen Nerv getroffen hat, weil ich mir vorstelle, dass eine sequenzielle Auflistung von Büchern etwas ist, das einem Array durch XML-Serialisierung zugeordnet werden könnte. Ich habe mich entschieden, dies zu versuchen, indem ich das unten gezeigte Schema in eine C#-Klasse konvertierte.

<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
targetNamespace="urn:xmlns:25hoursaday-com:my-bookshelf" 
xmlns:bk="urn:xmlns:25hoursaday-com:my-bookshelf" 
elementFormDefault="qualified">
   <xs:element name="books">
      <xs:complexType>
         <xs:sequence>
            <xs:element name="book" type="bk:bookType" 
maxOccurs="unbounded" />
         </xs:sequence>
      </xs:complexType>
   </xs:element>
   <xs:complexType name="bookType">
      <xs:sequence>
         <xs:element name="title" type="xs:string" />
         <xs:element name="author" type="xs:string" />
         <xs:element name="publication-date" type="xs:date" />
      </xs:sequence>
      <xs:attribute name="publisher" type="xs:string" />
      <xs:attribute name="on-loan" type="xs:string" />
   </xs:complexType>
</xs:schema>

Das .NET Framework SDK stellt das XML-Schemadefinitionstool xsd.exe bereit, mit dem XSD-Schemas in C#-Klassen konvertiert werden können. Ich habe meine Schemadatei in die C#-Quelldatei konvertiert und die folgenden Befehle in der Befehlszeile ausgegeben:

   xsd.exe  /c books.xsd 

Die generierte C#-Klasse wird mit Attributen ergänzt, die Informationen darüber bereitstellen, wie xmlSerializer die Klasse in XML konvertiert. Die Attribute XmlRootAttribute, XmlElementAttribute und XmlAttributeAttribute werden verwendet, um anzugeben, welche Klassen, Felder oder Eigenschaften die Stamm-, Element- und Attributknoten im generierten XML werden. Die aus dem Schema generierte Klasse wird unten gezeigt.

using System.Xml.Serialization;

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(Namespace=
"urn:xmlns:25hoursaday-com:my-bookshelf")]
[System.Xml.Serialization.XmlRootAttribute("books", 
Namespace="urn:xmlns:25hoursaday-com:my-bookshelf", IsNullable=false)]
public class books {
    
    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("book")]
    public bookType[] book;
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(Namespace=
"urn:xmlns:25hoursaday-com:my-bookshelf")]
public class bookType {
    
    /// <remarks/>
    public string title;
    
    /// <remarks/>
    public string author;
    
   /// <remarks/>
   [System.Xml.Serialization.XmlElementAttribute("publication-date", 
DataType="date")]      
    public System.DateTime publicationdate;

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string publisher;
    
    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute("on-loan")]
    public string onloan;
}

Hinweis Das Attribut, das das Publicationdate-Feld kommentiert, verfügt über eine DataType-Eigenschaft . Es gibt keinen Typ im .NET Framework, der dem Typ xs:date vollständig entspricht. Die nächste Übereinstimmung ist System.DateTime, die Datums- und Uhrzeitdaten speichert. Wenn Sie die DataType-Eigenschaft als "date" angeben, wird sichergestellt, dass xmlSerializer nur den Datumsteil des DateTime-Objekts serialisiert.

Der XmlSerializer hat die mehreren Buchelemente des Typs bookType in meinem Schema einem Array von bookType Objekten namens bookzugeordnet. Ich dachte, ich wäre bereit, meine Klassen an das DataGrid zu binden, bis ich einen Abschnitt der Dokumentation zu Datenquellen für das DataGrid-Steuerelement bemerkte, in dem darauf hingewiesen wurde, dass die Objekte in einem Array öffentliche Eigenschaften aufweisen müssen, wenn sie an ein DataGrid gebunden werden sollen. Da das XSD-Tool öffentliche Felder erstellt hat, musste ich meine Klassen korrigieren, indem ich die Felder privat machte und sie stattdessen über Eigenschaften verfügbar machte. Dann musste ich auch die XML-Serialisierungsspezifischen Attribute aus den Feldern in die Eigenschaften verschieben, da der XmlSerializer nur in öffentlichen Feldern und Eigenschaften nach Attributen sucht. Unten sehen Sie die geänderte bookType Klasse.

   /// <remarks/>
   [System.Xml.Serialization.XmlTypeAttribute(Namespace=
"urn:xmlns:25hoursaday-com:my-bookshelf")]
   public class bookType 
   {
      /// <remarks/>
      private string _title;
      public string title{
         get{ return _title; }
         set { _title = value; }
      }
        
      /// <remarks/>
      private string _author;
      public string author
      {
         get{ return _author; }
         set { _author = value; }
      }
        
      /// <remarks/>
      private  System.DateTime _publicationdate;
      [System.Xml.Serialization.XmlElementAttribute("publication-date",
 DataType="date")]      
      public System.DateTime publicationdate
      {
         get{ return _publicationdate; }
         set { _publicationdate = value; }
      }
        
      
      private string _publisher;
      /// <remarks/>
      [System.Xml.Serialization.XmlAttributeAttribute()]
      public string publisher
      {
      
         get{ return _publisher; }
         set { _publisher = value; }
      }
        
      private  string _onloan;
      [System.Xml.Serialization.XmlAttributeAttribute("on-loan")]      
      public string onloan
      {
         get{ return _onloan; }
         set { _onloan = value; }
      }
   }

Mit den oben genannten Änderungen ist es jetzt möglich, meine Bücherliste als XML an ein DataGrid zu binden. Beim Auslösen des Visual Studio® .NET-Formular-Designers habe ich schnell ein DataGrid gezogen und auf einem Formular abgelegt, zusammen mit einigen Schaltflächen zu Navigationszwecken. Der letzte Schritt bestand darin, Code hinzuzufügen, um sicherzustellen, dass das DataGrid nach dem Laden des Formulars an mein XML gebunden wurde. Die Form_Load -Methode in meiner Klasse wird unten gezeigt.

private void Form1_Load(object sender, System.EventArgs e)
     {
       try{
         TextReader reader = new StreamReader("books.xml");
         XmlSerializer serializer = new XmlSerializer(typeof(books));
         myBooks = (books)serializer.Deserialize(reader);
         reader.Close();
        
         //currency manager used for cursoring through book array in UI
         currencyManager = 
(CurrencyManager)dataGrid1.BindingContext[myBooks.book];

         dataGrid1.DataSource= myBooks.book;

       }catch(XmlException xe){
         MessageBox.Show (xe.Message, "XML Parse Error", 
                MessageBoxButtons.OK, MessageBoxIcon.Error);

       }catch(InvalidOperationException ioe){
         MessageBox.Show (ioe.InnerException.Message, "XML 
Serialization Error", 
                MessageBoxButtons.OK, MessageBoxIcon.Error);

       }
     }

Und das ist alles, was es braucht. Unten sehen Sie das XML-Dokument, das ich mit meiner Anwendung bearbeiten möchte.

<books xmlns="urn:xmlns:25hoursaday-com:my-bookshelf" >
  <book publisher="QUE">
    <title>XML By Example</title>
    <author>Benoit Marchal</author>
    <publication-date>1999-12-31</publication-date>
  </book>
  <book publisher="Addison Wesley" on-loan="Dmitri">
    <title>Essential C++</title>
    <author>Stanley Lippman</author>
    <publication-date>2000-10-31</publication-date>
  </book>
  <book publisher="WROX">
    <title>XSLT Programmer's Reference</title>
    <author>Michael Kay</author>
    <publication-date>2001-04-30</publication-date>
  </book>
  <book publisher="Addison Wesley" on-loan="Sanjay">
    <title>Mythical Man Month</title>
    <author>Frederick Brooks</author>
    <publication-date>1995-06-30</publication-date>
  </book>
  <book publisher="Apress">
    <title>Programmer's Introduction to C#</title>
    <author>Eric Gunnerson</author>
    <publication-date>2001-06-30</publication-date>
  </book>
</books>

Abbildung 1 unten zeigt ein DataGrid, das an das obige XML-Dokument in meiner Book Inventory-Anwendung gebunden ist.

Abbildung 1. Die Anwendung "Buchbestand"

Beachten Sie, dass ich mit meiner Anwendung zwar den Inhalt meines XML-Dokuments bearbeiten kann, das an das DataGrid gebunden ist, aber ich meiner Bestandsdatei keine neuen Bücher hinzufügen oder löschen kann. Die Unfähigkeit, die Anzahl von Büchern über dataGrid zu ändern, ist eine Einschränkung, die bei der Bindung an Arrays auferlegt wird. Dieses Problem ist nicht vorhanden, wenn es an andere Datenquellen gebunden ist.

Aus Gründen der Vollständigkeit sollte ich einen alternativen Ansatz zur Lösung meines Datenbindungsproblems hervorheben. Ich hätte das XML-Dokument und das XML-Schema mit den Methoden ReadXml() bzw. ReadXmlSchema() in ein DataSet laden und dann an das DataGrid gebunden. Beschreibungen zur Verwendung dieses alternativen Ansatzes finden Sie in der .NET SDK-Dokumentation im Abschnitt Generieren relationaler DataSet-Struktur aus XML-Schema (XSD). Ein Codebeispiel, das die DataSet-Klasse anstelle der XML-Serialisierung verwendet, ist auch in der Downloaddatei für den Artikel enthalten.

Häufig gestellte Fragen zur XML-Serialisierung

Im Folgenden ist eine Liste der Häufig gestellten Fragen zur XML-Serialisierung in der microsoft.public.dotnet.xml Newsgroup im .NET Framework aufgeführt.

F: Warum kann ich .NET Framework Klassen wie Ausnahmen und Schriftarten nicht serialisieren?

Eine: Der XmlSerializer wurde in erster Linie mit zwei Zielen entwickelt: XML-Datenbindung an XSD-konforme Datenstrukturen und -vorgänge ohne spezielle Codezugriffsrechte. Diese beiden Ziele arbeiten mit xmlSerializer als universelle Objektpersistenzlösung für einige Arten von Objekten.

Die allgemeine Serialisierung erfordert möglicherweise den Zugriff auf private Felder, das Übergeben des Standardobjekterstellungsprozesses des Frameworks usw., was wiederum besondere Berechtigungen erfordert. Der SoapFormatter aus dem System.Runtime.Serialization.Formatters.Soap-Namespace stellt eine Alternative bereit, die diesen Einschränkungen nicht unterliegt, für den Betrieb jedoch eine vollständige Vertrauenswürdigung erforderlich ist. Außerdem wird ein XML-Format erzeugt, dessen Generation mithilfe der Attribute im System.Runtime.Remoting.Metadata-Namespace angepasst werden kann.

Der BinaryFormatter aus dem System.Runtime.Serialization.Formatters.Binary-Namespace kann auch als Mechanismus verwendet werden, um einfache Objektpersistenz und -transport für Situationen bereitzustellen, in denen die XML-Serialisierung Nicht Ihren Anforderungen entspricht.

F: Wie kann ich Klassen serialisieren, die nicht für die XML-Serialisierung entwickelt wurden, wenn ich soapFormatter nicht verwenden möchte?

Eine: Sie können spezielle Wrapperklassen entwerfen, die Felder und Eigenschaften im XmlSerializer verfügbar machen oder ausblenden.

F: Gewusst wie Auflistungen von Objekten serialisieren?

Eine: Der XmlSerializer löst eine Ausnahme aus, wenn die Auflistung Typen enthält, die nicht für den Konstruktor des XmlSerializer deklariert wurden. Ihre Möglichkeiten:

  1. Deklarieren Sie die Typen an das Serialisierungsprogramm, indem Sie einen Typ[] mit den Typen übergeben, die innerhalb der Auflistung zu erwarten sind.

    OR

  2. Implementieren Sie eine stark typisierte Auflistung, die von System.Collections.CollectionBase abgeleitet ist, mit einem Indexer, der der Add() -Methode entspricht.

F: Warum werden nicht alle Eigenschaften von Auflistungsklassen serialisiert?

Eine: Der XmlSerializer serialisiert die Elemente in der Auflistung nur, wenn die IEnumerable - oder die ICollection-Schnittstelle erkannt wird . Dieses Verhalten ist beabsichtigt. Die einzige Problembereinigung besteht darin, die benutzerdefinierte Auflistung in zwei Klassen umzugestalten, von denen eine die Eigenschaften verfügbar macht, einschließlich eines der reinen Auflistungstypen.

F: Warum kann ich Hashtabellen nicht serialisieren?

Eine: XmlSerializer kann keine Klassen verarbeiten, die die IDictionary-Schnittstelle implementieren. Dies war teilweise auf Zeitplaneinschränkungen zurückzuführen, zum anderen auf die Tatsache, dass eine Hashtabelle kein Pendant im XSD-Typsystem hat. Die einzige Lösung besteht darin, eine benutzerdefinierte Hashtabelle zu implementieren, die die IDictionary-Schnittstelle nicht implementiert.

F: Warum enthalten vom XmlSerializer ausgelöste Ausnahmen keine Details zum Fehler?

Eine: Sie enthalten zwar alle Informationen, aber sie werden in der InnerException-Eigenschaft der ausgelösten Ausnahme gespeichert, bei der es sich in der Regel um eine InvalidOperationExceptionhandelt. Im Allgemeinen sollte bei abgefangenen Ausnahmen immer ToString() aufgerufen werden, um die vollständigen Details der Ausnahme zu erhalten.

F: Welche Aspekte des W3C-XML-Schemas werden von XmlSerializer während der Konvertierung von Schemas in Klassen nicht unterstützt?

Eine: Der XmlSerializer unterstützt Folgendes nicht:

  • Alle einfachen Typeinschränkungsfacets neben der Enumeration.
  • Namespacebasierte Wildcards.
  • Identitätseinschränkungen.
  • Ersetzungsgruppen.
  • Blockierte Elemente oder Typen.

F: Warum unterstützt XSD.exe das schemaLocation-Attribut bei Importen und Eingeschlossenen nicht?

Eine: Die W3C-XML-Schemaempfehlung beschreibt dieses Attribut als Hinweis, der von Prozessoren ignoriert werden kann, die alternative Methoden zum Suchen von Schemas verwenden können. XSD.exe verwendet nur Schemas, die über die Befehlszeile angegeben werden, um das Schema A.xsd zu konvertieren, das schema B.xsd importiert.

xsd.exe /c A.xsd B.xsd 

Außerdem kann die wsdl.exe-Anwendung, die als Schwesteranwendung für xsd.exe gilt, aus dem Web heruntergeladen werden. Wenn Sie dies tun und die wsdl.exe verwenden, befolgen Sie schemaLocation-Hinweise in import and includes.

F: Was sind die Unterschiede zwischen der XSD-Zuordnung zur Laufzeittypzuordnung, die von der XML-Serialisierung verwendet wird, und denen, die von der ADO.NET- oder XSD-Validierung verwendet werden?

Eine: Die Zuordnungen von W3C-XML-Schematypen zu Laufzeittypen, die vom Dataset und der Schemaüberprüfung verwendet werden, werden im Thema Datentypunterstützung zwischen XML-Schematypen (XSD) und .NET Framework Types beschrieben.

Diese Zuordnung unterscheidet sich von denen, die verwendet werden, wenn XML-Serialisierungsattribute in den Feldern und Eigenschaften einer Klasse angegeben werden. Jedes xml-Serialisierungsattribute hat seine Zuordnung von -Objekten zu W3C-XML-Schematypen, die in der Beschreibung für die DataType-Eigenschaft für diese Klasse definiert sind. Dazu gehören die Beschreibungen der SoapAttributeAttribute.DataType-Eigenschaft , der SoapElementAttribute.DataType-Eigenschaft, der XmlArrayItemAttribute.DataType-Eigenschaft, der XmlAttributeAttribute.DataType-Eigenschaft, der XmlElementAttribute.DataType-Eigenschaft und der XmlRootAttribute.DataType-Eigenschaft.

Ein wichtiger Unterschied besteht darin, dass die gregorianischen Datumsangaben, z xs:gMonth . B. und xs:gYear, , Zeichenfolgen durch XML-Serialisierung zugeordnet werden, während die Validierung sie DateTime-Objekten zuordnet. Ein weiterer Unterschied besteht darin, dass die DTD-basierten Listentypen, z. B. IDREFS und NMTOKENS, Zeichenfolgen durch XML-Serialisierung zugeordnet werden, während die Überprüfung sie Arrays von Zeichenfolgen zuordnet. Der xs:duration Typ wird auch bei der XML-Serialisierung anders zugeordnet als bei der Schemavalidierung. Die XML-Serialisierung ordnet sie Zeichenfolgen zu, während sie bei der Überprüfung TimeSpan-Objekten zugeordnet werden. Der xs:integer Typ wird als Zahl ohne Ober- oder Untergrenze für seine Größe angegeben. Aus diesem Grund wird es weder bei der XML-Serialisierung noch bei der Validierung dem System.Int32-Typ zugeordnet. Stattdessen ordnet die XML-Serialisierung einer Zeichenfolge zu, während die xs:integer Überprüfung es dem Decimal-Typ zuordnet, der viel größer ist als alle ganzzahligen Typen im .NET Framework.

Zusammenfassung

Die von der XML-Serialisierung im .NET Framework bereitgestellten Features bieten die Möglichkeit, stark typisiertes XML nahtlos innerhalb der .NET Framework zu verarbeiten. Stark typisierte XML-Dokumente können die Vorteile der .NET Framework nutzen, z. B. die Datenbindung an Windows und ASP.NET Forms, ein umfangreiches Sammlungsframework und eine Auswahl an Programmiersprachen. All dies erfolgt unter Berücksichtigung der Unterstützung von W3C-Standards wie XML 1.0 und XML-Schema, was bedeutet, dass die Interoperabilität von XML beim Versuch, solche stark typisierten Dokumente zu übertragen oder beizubehalten, keine Kosten verursacht.

Danksagung

Ich möchte Doug Purdy und Stefan Pharies vom XML-Serialisierungsteam sowie Christoph Schittko für ihre Hilfe bei der Überprüfung und Bereitstellung von Inhalten für diesen Artikel danken.

Dare Obasanjo ist Mitglied des WebData-Teams von Microsoft, das unter anderem die Komponenten im System.Xml- und System.Data-Namespace der .NET Framework, Microsoft XML Core Services (MSXML) und Microsoft Data Access Components (MDAC) entwickelt.

Sie können alle Fragen oder Kommentare zu diesem Artikel auf dem Extreme XML-Nachrichtenboard auf GotDotNet posten.