pengembangan-web-mp-pd.com

Serialisasi .NET XML gotchas?

Saya mengalami beberapa gotcha ketika melakukan serialisasi C # XML yang saya pikir akan saya bagikan:


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();
        }
    }
}

Adakah XML Serialisasi yang didapat di luar sana?

120
Kalid

Gotcha besar lainnya: ketika mengeluarkan XML melalui halaman web (ASP.NET), Anda tidak ingin menyertakan nicode Byte-Order Mark . Tentu saja, cara untuk menggunakan atau tidak menggunakan BOM hampir sama:

BAD (termasuk BOM):

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

BAIK:

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

Anda dapat secara eksplisit memberikan false untuk menunjukkan Anda tidak menginginkan BOM. Perhatikan perbedaan yang jelas dan jelas antara Encoding.UTF8 dan UTF8Encoding.

Tiga BOM Bytes tambahan di awal adalah (0xEFBBBF) atau (239 187 191).

Referensi: http://chrislaco.com/blog/troubleshooting-common-problems-with-the-xmlserializer/

27
Kalid

Saya belum bisa memberikan komentar, jadi saya akan mengomentari posting Dr8k dan membuat pengamatan lain. Variabel pribadi yang terpapar sebagai properti pengambil/penyetel publik, dan mendapatkan serialisasi/deserialisasi seperti itu melalui properti-properti itu. Kami melakukannya di pekerjaan lama saya saat itu.

Satu hal yang perlu diperhatikan adalah bahwa jika Anda memiliki logika di properti tersebut, logika dijalankan, jadi terkadang, urutan serialisasi sebenarnya penting. Anggota --- secara implisit diurutkan berdasarkan bagaimana mereka dipesan dalam kode, tetapi tidak ada jaminan, terutama ketika Anda mewarisi objek lain. Memerintahkan mereka secara eksplisit adalah rasa sakit di bagian belakang.

Saya telah dibakar oleh ini di masa lalu.

21
Charles Graham

Ketika membuat serial menjadi string XML dari aliran memori, pastikan untuk menggunakan MemoryStream # ToArray () sebagai ganti MemoryStream # GetBuffer () atau Anda akan berakhir dengan karakter sampah yang tidak akan deserialisasi dengan benar (karena buffer tambahan dialokasikan).

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

15
realgt

IEnumerables<T> yang dihasilkan melalui pengembalian hasil tidak bisa serial. Ini karena kompiler menghasilkan kelas yang terpisah untuk menerapkan pengembalian hasil dan kelas itu tidak ditandai sebagai serializable.

10
bwillard

Jika serializer bertemu dengan anggota/properti yang memiliki antarmuka sebagai tipenya, ia tidak akan membuat serial. Misalnya, berikut ini tidak akan bersambung ke XML:

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

Meskipun ini akan membuat serial:

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

Anda tidak dapat membuat serial properti hanya baca. Anda harus memiliki pengambil dan penyetel, bahkan jika Anda tidak pernah bermaksud menggunakan deserialisasi untuk mengubah XML menjadi objek.

Untuk alasan yang sama, Anda tidak bisa membuat serial properti yang mengembalikan antarmuka: deserializer tidak akan tahu kelas konkret mana yang akan dipakai.

8
Tim Robinson

Oh, ini bagus: karena kode serialisasi XML dihasilkan dan ditempatkan di DLL terpisah, Anda tidak mendapatkan kesalahan berarti ketika ada kesalahan dalam kode Anda yang merusak serializer. Hanya sesuatu seperti "tidak dapat menemukan s3d3fsdf.dll". Bagus.

7
Eric Z Beard

Tidak dapat membuat cerita bersambung suatu objek yang tidak memiliki konstrutor tanpa parameter (baru saja digigit oleh yang itu).

Dan untuk beberapa alasan, dari properti berikut ini, Nilai akan diserialisasi, tetapi tidak FullName:

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

Saya tidak pernah sempat mencari tahu mengapa, saya baru saja mengubah Nilai menjadi internal ...

6
Benjol

Anda mungkin menghadapi masalah serialisasi objek jenis Warna dan/atau Font.

Berikut sarannya, yang membantu saya:

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

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

4
Max Galkin

Lihat " Skema XML Lanjutan Definisi Bahasa Atribut Dukungan Binding " untuk perincian tentang apa yang didukung oleh XML Serializer, dan untuk perincian tentang cara di mana fitur XSD yang didukung didukung.

4
John Saunders

Jika Anda mencoba untuk membuat serial array, List<T>, Atau IEnumerable<T> Yang berisi turunan dari subkelas dari T, Anda harus menggunakan XmlArrayItemAttribute untuk mendaftar semua subtipe yang digunakan. Kalau tidak, Anda akan mendapatkan System.InvalidOperationException Yang tidak membantu saat runtime saat Anda membuat serial.

Ini adalah bagian dari contoh lengkap dari dokumentasi

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

Jika Majelis yang dihasilkan Serialisasi XML Anda tidak dalam konteks Muatan yang sama dengan kode yang mencoba menggunakannya, Anda akan mengalami kesalahan yang luar biasa seperti:

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)

Penyebabnya bagi saya adalah plugin dimuat menggunakan konteks LoadFrom yang memiliki banyak kelemahan untuk menggunakan konteks Load. Agak menyenangkan melacak yang turun.

4
user7116

Properti yang ditandai dengan atribut Obsolete tidak diserialisasi. Saya belum menguji dengan atribut Deprecated tapi saya menganggap itu akan bertindak dengan cara yang sama.

3
James Hulse

Variabel/properti pribadi tidak diserialisasi dalam mekanisme default untuk serialisasi XML, tetapi dalam serialisasi biner.

3
Charles Graham

Jika XSD Anda menggunakan grup substitusi, kemungkinan Anda tidak dapat membatalkannya secara otomatis. Anda harus menulis serialiseri Anda sendiri untuk menangani skenario ini.

Misalnya.

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

Dalam contoh ini, Amplop dapat berisi Pesan. Namun, serializer default .NET tidak membedakan antara Message, ExampleMessageA dan ExampleMessageB. Ini hanya akan membuat cerita bersambung ke dan dari kelas Pesan dasar.

2
ilitirit

Berhati-hatilah dengan jenis serialisasi tanpa serialisasi eksplisit, ini dapat mengakibatkan penundaan sementara .Net membangunnya. Saya menemukan ini baru-baru ini sambil membuat serial RSAParameters .

2
Keith

Saya tidak dapat menjelaskan yang ini, tetapi saya menemukan ini tidak akan bersambung:

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

tetapi ini akan:

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

Dan juga patut dicatat bahwa jika Anda membuat serial ke memstream, Anda mungkin ingin mencari 0 sebelum menggunakannya.

2
annakata

Variabel/properti pribadi tidak diserialisasi dalam serialisasi XML, tetapi dalam serialisasi biner.

Saya percaya ini juga membuat Anda jika Anda mengekspos anggota pribadi melalui properti publik - anggota pribadi tidak mendapatkan serial sehingga anggota publik semua referensi nilai nol.

0
Dr8k