it-swarm-es.tech

¿Las trampas de la serialización de XML XML?

Me he topado con algunos errores al realizar la serialización C # XML que pensé que compartiría:


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

¿Alguna otra trampa de serialización XML por ahí?

120
Kalid

Otro gran problema: al generar XML a través de una página web (ASP.NET), no desea incluir el Unicode Byte-Order Mark . Por supuesto, las formas de usar o no usar la lista de materiales son casi las mismas:

MALO (incluye lista de materiales):

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

BUENO:

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

Puede pasar explícitamente false para indicar que no desea la lista de materiales. Observe la clara y obvia diferencia entre Encoding.UTF8 y UTF8Encoding.

Los tres bytes adicionales de la lista de materiales al principio son (0xEFBBBF) o (239 187 191).

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

27
Kalid

No puedo hacer comentarios todavía, así que comentaré sobre la publicación de Dr8k y haré otra observación. Las variables privadas que se exponen como propiedades públicas de captador/definidor, y se serializan/deserializan como tales a través de esas propiedades. Lo hicimos en mi antiguo trabajo todo el tiempo.

Sin embargo, una cosa a tener en cuenta es que si tiene alguna lógica en esas propiedades, la lógica se ejecuta, por lo que a veces, el orden de serialización realmente importa. Los miembros están implícitamente ordenados por la forma en que están ordenados en el código, pero no hay garantías, especialmente cuando está heredando otro objeto. Su pedido explícito es un dolor en la parte trasera.

He sido quemado por esto en el pasado.

21
Charles Graham

Al serializar en una cadena XML desde un flujo de memoria, asegúrese de usar MemoryStream # ToArray () en lugar de MemoryStream # GetBuffer () o terminará con caracteres basura que no se deserializarán adecuadamente (debido al búfer adicional asignado).

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

15
realgt

IEnumerables<T> que se generan a través de rendimientos de rendimiento no son serializables. Esto se debe a que el compilador genera una clase separada para implementar el rendimiento y esa clase no está marcada como serializable.

10
bwillard

Si el serializador encuentra un miembro/propiedad que tiene una interfaz como su tipo, no se serializará. Por ejemplo, lo siguiente no se serializará a XML:

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

Aunque esto se serializará:

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

No puedes serializar propiedades de solo lectura. Debe tener un captador y un configurador, incluso si nunca tiene la intención de utilizar la deserialización para convertir XML en un objeto.

Por la misma razón, no puede serializar propiedades que devuelven interfaces: el deserializador no sabría qué clase concreta crear.

8
Tim Robinson

Oh, aquí hay una buena: dado que el código de serialización XML se genera y se coloca en una DLL separada, no obtiene ningún error significativo cuando hay un error en su código que rompe el serializador. Solo algo como "no se puede localizar s3d3fsdf.dll". Bonito.

7
Eric Z Beard

No se puede serializar un objeto que no tenga un constructor sin parámetros (solo fue mordido por ese).

Y por alguna razón, a partir de las siguientes propiedades, el valor se serializa, pero no FullName:

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

Nunca llegué a entender por qué, simplemente cambié el valor a interno ...

6
Benjol

Una cosa más a tener en cuenta: no puede serializar miembros de clase privados/protegidos si está utilizando la serialización XML "predeterminada".

Pero puede especificar la lógica de serialización XML personalizada implementando IXmlSerializable en su clase y serializar cualquier campo privado que necesite/desee.

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

5
Max Galkin

Es posible que tenga problemas al serializar objetos de tipo Color y/o Fuente.

Aquí están los consejos, que me ayudaron:

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

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

4
Max Galkin

Consulte " Atributos del lenguaje de definición de esquema XML avanzado Compatibilidad con el enlace " para obtener información detallada sobre qué es compatible con el serializador XML y sobre cómo se admiten las funciones XSD compatibles.

4
John Saunders

Si intenta serializar una matriz, List<T>, o IEnumerable<T> que contiene instancias de subclasses of T, necesita usar XmlArrayItemAttribute para enumerar todos los subtipos que se están utilizando. De lo contrario, obtendrá un System.InvalidOperationException inútil en el tiempo de ejecución cuando serialice.

Aquí es parte de un ejemplo completo de la documentación

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

Si su ensamblaje XML generado por serialización no está en el mismo contexto de carga que el código que intenta usarlo, se encontrarán con errores asombrosos como:

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)

La causa de esto fue un complemento cargado utilizando LoadFrom context que tiene muchas desventajas al usar el contexto Load. Bastante divertido rastrearlo.

4
user7116

Las propiedades marcadas con el atributo Obsolete no se serializan. No he probado con el atributo Deprecated pero asumo que actuaría de la misma manera.

3
James Hulse

Las variables/propiedades privadas no se serializan en el mecanismo predeterminado para la serialización XML, pero están en serialización binaria.

3
Charles Graham

Si su XSD utiliza grupos de sustitución, es probable que no pueda (des) serializarlo automáticamente. Tendrá que escribir sus propios serializadores para manejar este escenario.

P.ej.

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

En este ejemplo, un Sobre puede contener Mensajes. Sin embargo, el serializador predeterminado de .NET no distingue entre Message, ExampleMessageA y ExampleMessageB. Solo se serializará hacia y desde la clase de mensaje base.

2
ilitirit

Tenga cuidado al serializar tipos sin una serialización explícita, ya que puede generar demoras mientras .Net los construye. Descubrí esto recientemente al serializar RSAParameters .

2
Keith

Realmente no puedo explicar esto, pero encontré que esto no se serializará:

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

pero esto va a:

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

Y también vale la pena señalar que si está serializando un memstream, es posible que desee buscar un 0 antes de usarlo.

2
annakata

Las variables/propiedades privadas no se serializan en la serialización XML, pero están en serialización binaria.

Creo que esto también lo afecta a usted si está exponiendo a los miembros privados a través de propiedades públicas: los miembros privados no se serializan, por lo que todos los miembros públicos hacen referencia a valores nulos.

0
Dr8k