it-swarm-eu.dev

.NET XML-Serialisierungsprobleme?

Bei der C # XML-Serialisierung sind mir einige Probleme aufgefallen, von denen ich dachte, ich würde sie teilen:


using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
{      
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
            return;

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");

            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement("item");

            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();

            writer.WriteStartElement("value");
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
}

Gibt es noch andere Probleme mit der XML-Serialisierung?

120
Kalid

Ein weiteres großes Problem: Wenn Sie XML über eine Webseite (ASP.NET) ausgeben, möchten Sie das nicode Byte-Order Mark nicht einschließen. Die Möglichkeiten, die Stückliste zu verwenden oder nicht zu verwenden, sind natürlich fast gleich:

SCHLECHT (einschließlich Stückliste):

XmlTextWriter wr = new XmlTextWriter(stream, new System.Text.Encoding.UTF8);

GUT:

XmlTextWriter  wr = new XmlTextWriter(stream, new System.Text.UTF8Encoding(false))

Sie können explizit false übergeben, um anzugeben, dass Sie die Stückliste nicht möchten. Beachten Sie den klaren, offensichtlichen Unterschied zwischen Encoding.UTF8 und UTF8Encoding.

Die drei zusätzlichen Stücklistenbytes am Anfang sind (0xEFBBBF) oder (239 187 191).

Referenz: http://chrislaco.com/blog/troubleshooter-common-problems-with-the-xmlserializer/

27
Kalid

Ich kann noch keine Kommentare abgeben, daher werde ich den Beitrag von Dr8k kommentieren und eine weitere Bemerkung machen. Private Variablen, die als öffentliche Get-/Set-Eigenschaften verfügbar gemacht werden und als solche über diese Eigenschaften serialisiert/deserialisiert werden. Wir haben es die ganze Zeit bei meiner alten Arbeit gemacht.

Beachten Sie jedoch, dass die Logik ausgeführt wird, wenn in diesen Eigenschaften eine Logik vorhanden ist. In einigen Fällen ist die Reihenfolge der Serialisierung von Bedeutung. Die Mitglieder sind implizit nach ihrer Reihenfolge im Code geordnet, es gibt jedoch keine Garantien, insbesondere wenn Sie ein anderes Objekt erben. Sie explizit zu bestellen ist ein Schmerz im Heck.

Das hat mich in der Vergangenheit verbrannt.

21
Charles Graham

Verwenden Sie beim Serialisieren in eine XML-Zeichenfolge aus einem Speicherdatenstrom MemoryStream # ToArray () anstelle von MemoryStream # GetBuffer (). Andernfalls werden Junk-Zeichen (aufgrund des zugewiesenen zusätzlichen Puffers) nicht ordnungsgemäß deserialisiert.

http://msdn.Microsoft.com/en-us/library/system.io.memorystream.getbuffer (VS.80) .aspx

15
realgt

IEnumerables<T>, die über Renditeerträge generiert werden, sind nicht serialisierbar. Dies liegt daran, dass der Compiler eine separate Klasse generiert, um Yield Return zu implementieren, und diese Klasse nicht als serialisierbar markiert ist.

10
bwillard

Wenn der Serializer auf ein Mitglied/eine Eigenschaft stößt, das/die eine Schnittstelle als Typ hat, wird keine Serialisierung durchgeführt. Beispiel: Folgendes wird nicht in XML serialisiert:

public class ValuePair
{
    public ICompareable Value1 { get; set; }
    public ICompareable Value2 { get; set; }
}

Dies wird jedoch serialisieren:

public class ValuePair
{
    public object Value1 { get; set; }
    public object Value2 { get; set; }
}
10
Allon Guralnek

Sie können keine schreibgeschützten Eigenschaften serialisieren. Sie müssen über einen Getter und einen Setter verfügen, auch wenn Sie niemals beabsichtigen, XML mithilfe der Deserialisierung in ein Objekt zu verwandeln.

Aus dem gleichen Grund können Sie keine Eigenschaften serialisieren, die Schnittstellen zurückgeben: Der Deserialisierer würde nicht wissen, welche konkrete Klasse zu instanziieren ist.

8
Tim Robinson

Hier ist ein guter Tipp: Da der XML-Serialisierungscode generiert und in einer separaten DLL abgelegt wird, wird kein aussagekräftiger Fehler angezeigt, wenn in Ihrem Code ein Fehler vorliegt, der den Serialisierer beschädigt. Nur so etwas wie "s3d3fsdf.dll kann nicht gefunden werden". Nett.

7
Eric Z Beard

Ein Objekt ohne parameterlosen Construtor kann nicht serialisiert werden (wurde gerade von diesem gebissen).

Aus irgendeinem Grund wird Value aus den folgenden Eigenschaften serialisiert, nicht jedoch FullName:

    public string FullName { get; set; }
    public double Value { get; set; }

Ich bin nie dazu gekommen, herauszufinden, warum, sondern habe Value in internal geändert ...

6
Benjol

Noch etwas zu beachten: Sie können private/geschützte Klassenmitglieder nicht serialisieren, wenn Sie die "standardmäßige" XML-Serialisierung verwenden.

Sie können jedoch eine benutzerdefinierte XML-Serialisierungslogik angeben, die IXmlSerializable in Ihrer Klasse implementiert, und alle privaten Felder serialisieren, die Sie benötigen/möchten.

http://msdn.Microsoft.com/en-us/library/system.xml.serialization.ixmlserializable.aspx

5
Max Galkin

Möglicherweise treten Probleme beim Serialisieren von Objekten des Typs Farbe und/oder Schriftart auf.

Hier sind die Ratschläge, die mir geholfen haben:

http://www.codeproject.com/KB/XML/xmlsettings.aspx

http://www.codeproject.com/KB/cs/GenericXmlSerializition.aspx

4
Max Galkin

Unter " Erweiterte Unterstützung für die Bindung von XML-Schemadefinitionssprachenattributen " finden Sie Informationen zur Unterstützung des XML-Serializers und zur Unterstützung der unterstützten XSD-Funktionen.

4
John Saunders

Wenn Sie versuchen, ein Array zu serialisieren, wird List<T>, oder IEnumerable<T>, das Instanzen von Unterklassen von T enthält, müssen Sie XmlArrayItemAttribute verwenden, um alle aufzulisten verwendete Subtypen. Andernfalls erhalten Sie ein nicht hilfreiches System.InvalidOperationException zur Laufzeit, wenn Sie serialisieren.

Hier ist ein Teil eines vollständigen Beispiels aus der Dokumentation

public class Group
{  
   /* The XmlArrayItemAttribute allows the XmlSerializer to insert both the base 
      type (Employee) and derived type (Manager) into serialized arrays. */

   [XmlArrayItem(typeof(Manager)), XmlArrayItem(typeof(Employee))]
   public Employee[] Employees;
4
MarkJ

Wenn sich Ihre durch XML-Serialisierung generierte Assembly nicht im selben Ladekontext befindet wie der Code, der versucht, sie zu verwenden, werden Sie auf großartige Fehler stoßen wie:

System.InvalidOperationException: There was an error generating the XML document.
---System.InvalidCastException: Unable to cast object
of type 'MyNamespace.Settings' to type 'MyNamespace.Settings'. at
Microsoft.Xml.Serialization.GeneratedAssembly.
  XmlSerializationWriterSettings.Write3_Settings(Object o)

Die Ursache dafür war für mich ein Plugin, das mit LoadFrom context geladen wurde, was viele Nachteile bei der Verwendung des Load-Kontexts hat. Ziemlich viel Spaß beim Aufspüren.

4
user7116

Eigenschaften, die mit dem Attribut Obsolete gekennzeichnet sind, werden nicht serialisiert. Ich habe noch nicht mit dem Deprecated Attribut getestet, aber ich gehe davon aus, dass es genauso funktionieren würde.

3
James Hulse

Private Variablen/Eigenschaften werden im Standardmechanismus für die XML-Serialisierung nicht serialisiert, befinden sich jedoch in der binären Serialisierung.

3
Charles Graham

Wenn Ihre XSD Substitutionsgruppen verwendet, können Sie sie möglicherweise nicht automatisch (de) serialisieren. Sie müssen Ihre eigenen Serializer schreiben, um dieses Szenario zu handhaben.

Z.B.

<xs:complexType name="MessageType" abstract="true">
    <xs:attributeGroup ref="commonMessageAttributes"/>
</xs:complexType>

<xs:element name="Message" type="MessageType"/>

<xs:element name="Envelope">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
            <xs:element ref="Message" minOccurs="0" maxOccurs="unbounded"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageA" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

<xs:element name="ExampleMessageB" substitutionGroup="Message">
    <xs:complexType mixed="false">
        <xs:complexContent mixed="false">
                <xs:attribute name="messageCode"/>
        </xs:complexContent>
    </xs:complexType>
</xs:element>

In diesem Beispiel kann ein Umschlag Nachrichten enthalten. Das Standardserialisierungsprogramm von .NET unterscheidet jedoch nicht zwischen Message, ExampleMessageA und ExampleMessageB. Es wird nur zur und von der Basis-Message-Klasse serialisiert.

2
ilitirit

Seien Sie vorsichtig beim Serialisieren von Typen ohne explizite Serialisierung, da dies zu Verzögerungen führen kann, während .Net sie erstellt. Ich habe das kürzlich entdeckt während ich RSAParameters serialisierte .

2
Keith

Ich kann das nicht wirklich erklären, aber ich habe festgestellt, dass es nicht serialisiert werden kann:

[XmlElement("item")]
public myClass[] item
{
    get { return this.privateList.ToArray(); }
}

aber das wird:

[XmlElement("item")]
public List<myClass> item
{
    get { return this.privateList; }
}

Beachten Sie auch, dass Sie bei der Serialisierung für einen Memstream möglicherweise auf 0 setzen möchten, bevor Sie ihn verwenden.

2
annakata

Private Variablen/Eigenschaften werden bei der XML-Serialisierung nicht serialisiert, sondern bei der binären Serialisierung.

Ich glaube, das bringt Sie auch, wenn Sie die privaten Mitglieder durch öffentliche Eigenschaften verfügbar machen - die privaten Mitglieder werden nicht serialisiert, sodass alle öffentlichen Mitglieder auf Nullwerte verweisen.

0
Dr8k