serializar objeto c# xml-serialization

c# - objeto - ¿Obstáculos de serialización XML.NET?



serializar objeto c# xml (19)

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

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

Me he encontrado con algunas trampas al hacer la serialización de C # XML que pensaba compartir:

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

¿Hay otros atracos de serialización XML por ahí?




Las propiedades marcadas con el atributo Obsolete no están serializadas. No probé con el atributo Deprecated pero supongo que actuaría de la misma manera.


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


No puede serializar las propiedades de solo lectura. Debe tener un getter y un setter, 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.


No se puede serializar un objeto que no tiene un construtor sin parámetros (acaba de ser mordido por ese).

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

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

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


Oh, este es uno bueno: dado que el código de serialización XML se genera y se coloca en una DLL separada, no se obtiene ningún error significativo cuando hay un error en el código que rompe el serializador. Algo así como "no se puede localizar s3d3fsdf.dll". Bonito.


Otra gran sorpresa: cuando se genera XML a través de una página web (ASP.NET), no se debe incluir la marca de pedido de bytes Unicode . Por supuesto, las formas de usar o no la BOM son casi las mismas:

MALO (incluye BOM):

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

BUENO:

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

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

Los tres bytes BOM adicionales al principio son (0xEFBBBF) o (239 187 191).

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



Realmente no puedo explicar esto, pero descubrí que esto no se serializará:

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

pero esto:

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

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


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

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

Aunque esto serializará:

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


Si intenta serializar una matriz, List<T> o IEnumerable<T> que contiene instancias de subclases de T , debe usar XmlArrayItemAttribute para enumerar todos los subtipos que se utilizan. De lo contrario, obtendrás una System.InvalidOperationException inútil en el tiempo de ejecución cuando serialices.

Aquí hay parte de un ejemplo completo de XmlArrayItemAttribute

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;


Si su XSD hace uso de grupos de sustitución, es probable que no pueda (de) 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á desde y hacia la clase de mensaje base.


Si su ensamblado generado de Serialización XML no está en el mismo contexto de carga que el código que intenta usarlo, se encontrará con errores increíbles 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 para mí fue un complemento cargado usando el contexto LoadFrom, que tiene muchas desventajas al usar el contexto Cargar. Bastante divertido rastreando eso.


Tenga cuidado con los tipos de serialización sin serialización explícita, puede ocasionar retrasos mientras .Net los construye. Descubrí esto recientemente al serializar RSAParameters .


Todavía no puedo hacer comentarios, así que comentaré sobre la publicación de Dr8k y haré otra observación. Las variables privadas que están expuestas como propiedades getter / setter públicas, y se serializan / deserializan como tales a través de esas propiedades. Lo hicimos en mi antiguo trabajo al mismo 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 la serialización realmente importa. Los miembros están ordenados implícitamente por la forma en que están ordenados en el código, pero no hay garantías, especialmente cuando heredas otro objeto. Ordenarlos explícitamente es un dolor en la retaguardia.

Me he quemado por esto en el pasado.



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