sharp serialize serializar objeto deserializar create c# .net serialization xml-serialization xmlserializer

serializar - serialize object to xml c#



ShouldSerialize*() vs*Patrón de serialización condicional especificado (2)

La intención del patrón {propertyName}Specified se documenta en el Soporte de enlace de esquema XML: Soporte de enlace de atributos MinOccurs . Se agregó para admitir un elemento de esquema XSD en el que:

  • El <element> está involucrado.
  • minOccurs es cero.
  • El atributo maxOccurs dicta una sola instancia.
  • El tipo de datos se convierte en un tipo de valor.

En este caso, xsd.exe /classes generará automáticamente (o puede generar manualmente) una propiedad con el mismo nombre que el elemento de esquema y una {propertyName}Specified booleana {propertyName}Specified get / set que rastrea si el elemento se encontró en el XML y se debe volver a serializar a XML. Si se encuentra el elemento, {propertyName}Specified se establece en true , de lo contrario es false . Por lo tanto, la instancia deserializada puede determinar si la propiedad no se configuró (en lugar de establecer explícitamente su valor predeterminado) en el XML original.

El inverso también se implementa para la generación de esquemas. Si define un tipo de C # con un par de propiedades que coinciden con el patrón anterior, luego use xsd.exe para generar un archivo XSD correspondiente, se minOccurrs un minOccurrs apropiado al esquema. Por ejemplo, dado el siguiente tipo:

public class ExampleClass { [XmlElement] public decimal Something { get; set; } [XmlIgnore] public bool SomethingSpecified { get; set; } }

Se generará el siguiente esquema, y ​​viceversa:

<xs:schema elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="ExampleClass" nillable="true" type="ExampleClass" /> <xs:complexType name="ExampleClass"> <xs:sequence> <xs:element minOccurs="0" maxOccurs="1" name="Something" type="xs:decimal" /> </xs:sequence> </xs:complexType> </xs:schema>

Tenga en cuenta que, aunque xsd.exe está documentado solo para generar automáticamente una {propertyName}Specified para las propiedades de tipo de valor, XmlSerializer respetará el patrón cuando se use manualmente para las propiedades de tipo de referencia.

Puede preguntar, ¿por qué xsd.exe no se une a un Nullable<T> en este caso? Quizás porque:

xsd.exe tener en cuenta este patrón porque a veces xsd.exe lo generará automáticamente, sin embargo, la interacción entre una propiedad y su propiedad Specified es extraña y puede producir errores. Puede completar todas las propiedades de su clase, luego serializar a XML y perder todo porque también no configuró las propiedades Specified correspondientes en true . Este "problema" aparece aquí de vez en cuando, vea, por ejemplo, esta pregunta o también esta .

Otro "problema" con este patrón es que, si necesita serializar su tipo con un serializador que no admite este patrón, es posible que desee suprimir manualmente la salida de esta propiedad durante la serialización, y probablemente deba configurarlo manualmente durante la deserialización . Dado que cada serializador puede tener su propio mecanismo personalizado para suprimir propiedades (¡o ningún mecanismo en absoluto!), Hacer esto puede volverse más y más pesado con el tiempo.

(Finalmente, estoy un poco sorprendido de que su MyPropertySpecified funcione correctamente sin un configurador. Parece que recuerdo una versión de .Net 2.0 en la que un MyPropertySpecified {propertyName}Specified faltante provocaría una excepción. Pero ya no es reproducible en versiones posteriores, y no tengo 2.0 para probar. Así que podría ser un tercer problema).

La compatibilidad con el método ShouldSerialize{PropertyName}() está documentada en Propiedades en Controles de formularios Windows Forms: definición de valores predeterminados con los métodos ShouldSerialize y Reset . Como puede ver, la documentación está en la sección Formularios de Windows de MSDN, no en la sección XmlSerializer , por lo que es, de hecho, una funcionalidad semi-oculta. No tengo idea de por qué existen soporte para este método y la propiedad Specified en XmlSerializer . ShouldSerialize se introdujo en .Net 1.1 y creo que el soporte de enlace MinOccurs se agregó en .Net 2.0 , por lo que tal vez la funcionalidad anterior no satisfizo las necesidades (o el gusto) del equipo de desarrollo de xsd.exe ?

Debido a que es un método, no una propiedad, carece de las "trampas" del patrón {propertyName}Specified . También parece ser más popular en la práctica, y ha sido adoptado por otros serializadores que incluyen:

Entonces, ¿qué patrón usar?

  1. Si xsd.exe genera una {propertyName}Specified para usted automáticamente, o si su tipo necesita rastrear si un elemento específico apareció o no en el archivo XML, o si necesita su XSD generado automáticamente para indicar que cierto valor es opcional, usa este patrón y ten cuidado con las "trampas".

  2. De lo contrario, use el patrón ShouldSerialize{PropertyName}() . Tiene menos problemas y puede ser más ampliamente compatible.

Soy consciente tanto del patrón ShouldSerialize * como del patrón * especificado y cómo funcionan, pero ¿hay alguna diferencia entre los dos?

¿Hay alguna "trampa" usando un método frente al otro cuando ciertas cosas deben ser serializadas condicionalmente?

Esta pregunta es específica para el uso de XmlSerializer , pero la información general sobre este tema también es bienvenida.

Hay muy poca información sobre este tema, por lo que puede deberse a que realizan exactamente el mismo propósito y es una elección de estilo. Sin embargo, parece extraño que los implementadores de .NET analicen la clase a través de la reflexión y busquen uno o ambos patrones para determinar cómo se comporta el serializador generado, ya que ralentiza la generación del serializador a menos que sea solo un artefacto de compatibilidad con versiones anteriores.

EDITAR: Para aquellos que no están familiarizados con los dos patrones si la propiedad *Specified o el método ShouldSerialize* devuelve verdadero, entonces esa propiedad se serializa.

public string MyProperty { get; set; } //*Specified Pattern [XmlIgnore] public bool MyPropertySpecified { get{ return !string.IsNullOrWhiteSpace(this.MyProperty); } } //ShouldSerialize* Pattern public bool ShouldSerializeMyProperty() { return !string.IsNullOrWhiteSpace(this.MyProperty); }


Para agregar a la respuesta muy detallada de @dbc, me encontré con un problema con la gestión de la serialización en clases derivadas. En mi situación, tenía una clase base y una clase derivada donde se Prop una propiedad Prop .

public class BaseClass { public virtual string Prop {get; set;} } public class Derived: BaseClass { public string Comp1 {get; set;} public string Comp2 {get; set;} public override string Prop {get => Comp1 + Comp2; set {}} }

Dado que se calcula la propiedad Prop en la clase derivada, para la clase Derived quería serializar Comp1 y Comp2 pero no Prop . Resulta que establecer el atributo XmlIgnore en la propiedad Prop en la clase Derived no funciona y Prop se serializa de todos modos.

También intenté agregar un método ShouldSerializeProp y una propiedad PropSpecified en la clase Derived , pero ninguno funciona. Intenté establecer puntos de interrupción para ver si se llaman y no se llaman.

Resulta que XmlSerializer está mirando la clase original donde la propiedad Prop aparece por primera vez en la jerarquía de clases para decidir si se serializa una propiedad o no. Para poder controlar la serialización en una clase derivada, primero tuve que agregar un virtual ShouldSerializeProp en la clase Base .

public class Base { ..... public virtual bool ShouldSerializeProp() {return true;} }

Entonces podría anular el ShouldSerializeProp en la clase Derived y devolver falso.

public class Derived: Base { ..... public override bool ShouldSerializeProp() {return false;} }

Este patrón permite que diferentes clases derivadas elijan qué propiedades de la clase primaria serializan. Espero que esto ayude.