La serialización XML en .NET permite objetos polimórficos a través del parámetro extraTypes[]
del constructor XmlSerializer
. También permite la personalización de la serialización XML para los tipos que implementan IXmlSerializable
.Tipos polimórficos e IXmlSerializable
Sin embargo, no soy capaz de combinar estas dos características - como se demuestra en este ejemplo mínima:
using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
namespace CsFoo
{
public class CustomSerializable : IXmlSerializable
{
public XmlSchema GetSchema() { return null; }
public void ReadXml(XmlReader xr) { }
public void WriteXml(XmlWriter xw) { }
}
class CsFoo
{
static void Main()
{
XmlSerializer xs = new XmlSerializer(
typeof(object),
new Type[] { typeof(CustomSerializable) });
xs.Serialize(new StringWriter(), new CustomSerializable());
}
}
La última línea de tiros System.InvalidOperationException
con este mensaje:
El tipo CsFoo.CustomSerializable no se puede usar en este contexto para usar CsFoo.CustomSerializable como parámetro, devolver tipo, o miembro de una clase o estructura, el parámetro, devolver tipo, o el miembro se debe declarar como ty pe CsFoo.CustomSerializable (no puede ser objeto). Los objetos de tipo CsFoo.CustomSerializable no se pueden usar en colecciones sin tipeo, como ArrayLists.
vadear a través de los conjuntos XML generados dinámicamente, en última instancia, nos volvemos a código de la biblioteca estándar de .NET llamando:
System.Xml.Serialization.XmlSerializationWriter.WriteTypedPrimitive(
String, String, Object, Boolean) : Void
A su vez, esto conduce a:
protected Exception CreateUnknownTypeException(Type type)
{
if (typeof(IXmlSerializable).IsAssignableFrom(type))
{
return new InvalidOperationException(
Res.GetString("XmlInvalidSerializable",
new object[] { type.FullName }));
}
// Rest omitted...
espectáculos Reflector que el recurso XmlInvalidSerializable
se corresponde con la cadena anterior, es decir, WriteTypedPrimitive
no le gusta IXmlSerializable
.
Si generamos un serializador no polimórficos, así:
XmlSerializer xs = new XmlSerializer(typeof(CustomSerializable));
.NET generará una llamada a:
System.Xml.Serialization.XmlSerializationWriter.WriteSerializable(
IXmlSerializable, String, String, Boolean) : Void
Este se encarga de IXmlSerializable
correctamente. ¿Alguien sabe por qué .NET no usa esta función en el caso polimórfico? Mirando el C# que genera el serializador XML, me parece que esto se puede hacer con bastante facilidad. Aquí hay un código que obtuve del serializador XML, con una solución probada:
void Write1_Object(string n, string ns, global::System.Object o,
bool isNullable, bool needType)
{
if ((object)o == null)
{
if (isNullable) WriteNullTagLiteral(n, ns);
return;
}
if (!needType)
{
System.Type t = o.GetType();
if (t == typeof(global::System.Object))
{
}
>>> patch begin <<<
+ else if (typeof(IXmlSerializable).IsAssignableFrom(t))
+ {
+ WriteSerializable((System.Xml.Serialization.IXmlSerializable)
((global::CsFoo.CustomSerializable)o),
+ @"CustomSerializable", @"", true, true);
+ }
>>> patch end <<<
else
{
WriteTypedPrimitive(n, ns, o, true);
return;
}
}
WriteStartElement(n, ns, o, false, null);
WriteEndElement(o);
}
Es éste queda fuera por razones técnicas o simplemente una limitación característica? Característica no admitida, o mi idiotez Mis intertubes habilidades de Google me fallan.
Encontré algunas preguntas relacionadas aquí, con "C# Xml-Serializing a derived class using IXmlSerializable" siendo más relevante. Me lleva a creer que simplemente no es posible.
En ese caso, mi pensamiento actual es inyectar una implementación predeterminada IXmlSerializable
en la clase base raíz. Entonces todo será un IXmlSerializable
, y .NET no se quejará. Puedo usar Reflection.Emit para batir los cuerpos ReadXml
y WriteXml
para cada tipo concreto, generando XML que se vería igual a como lo haría si usara el de la biblioteca.
Algunas personas, al enfrentarse con un problema de serialización XML, piensan "Lo sé, usaré Reflection.Emit para generar código". Ahora ellos tienen dos problemas.
P.S. Nota; Conozco las alternativas a la serialización XML de .NET, y sé que tiene limitaciones. También sé que guardar un POCO es mucho más simple que tratar con tipos de datos abstractos. Pero tengo un montón de código heredado y necesito soporte para los esquemas XML existentes.
Así que aunque aprecio las respuestas que muestran lo fácil que es en SomeOtherXML
, YAML
, XAML
, ProtocolBuffers
, DataContract
, RandomJsonLibrary
, Thrift
, o en su biblioteca MorseCodeBasedSerializeToMp3
- Hey yo podría aprender algo -, lo que estoy esperando es una Solución de problemas del serializador de XML, si no.
Aunque es una adición bastante tardía, de todos modos: la solución con tipos adicionales no funciona para mí en el caso de IXmlSerializable. Sin embargo [este enfoque] (http://www.softwarerockstar.com/2006/12/using-ixmlserializable-to-overcome-not-expected-error-on-derived-classes/) está funcionando, ya que permite la clase deserializada ser diferente de la clase de propiedad (!), por lo que cualquier lógica personalizada que encuentre el tipo correcto para crear se puede implementar de esa manera. – Vlad
Hay otra solución declarada [aquí] (https://connect.microsoft.com/VisualStudio/feedback/details/422577/incorrect-deserialization-of-polymorphic-type-that-implements-ixmlserializable) (utilizando el atributo 'XmlSchemaProvider' para enlazar tipos xml a los tipos .net), pero carece del código, y no pude hacer que esa idea funcionara. ¿Nadie? – Vlad