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:
-
Los valores nulables se utilizan para admitir el
xsi:nil="true"
. Ver Xsi: nil Attribute Binding Support . - Los valores anulables no se introdujeron hasta .Net 2.0, por lo que ¿quizás fue demasiado tarde para usarlos para este propósito?
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:
- Json.NET
- protobuf-net (que afirma ser compatible con ambos patrones ).
Entonces, ¿qué patrón usar?
-
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". -
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.