Esto es simplemente una limitación inherente de la serialización declarativa donde la información de tipo no está incrustada en la salida.
Al intentar convertir de <Flibble Foo="10" />
nuevo en
public class Flibble { public object Foo { get; set; } }
¿Cómo sabe el serializador si debe ser un int, una cadena, un doble (o algo más) ...
Para que esto funcione, tiene varias opciones, pero si realmente no sabe hasta el tiempo de ejecución, es probable que la forma más fácil de hacerlo sea utilizando XmlAttributeOverrides .
Lamentablemente, esto solo funcionará con clases base, no interfaces. Lo mejor que puede hacer es ignorar la propiedad que no es suficiente para sus necesidades.
Si realmente debes quedarte con las interfaces, tienes tres opciones reales:
Escóndelo y trátalo en otra propiedad.
Placa de caldera fea, desagradable y mucha repetición, pero la mayoría de los consumidores de la clase no tendrán que lidiar con el problema:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { }
set { }
}
Es probable que esto se convierta en una pesadilla de mantenimiento ...
Implementar IXmlSerializable
Similar a la primera opción en que usted toma el control total de las cosas pero
- Pros
- No tienes desagradables propiedades "falsas" dando vueltas.
- puede interactuar directamente con la estructura xml agregando flexibilidad / control de versiones
- Contras
- puede terminar teniendo que volver a implementar la rueda para todas las demás propiedades de la clase
Los problemas de duplicación de esfuerzos son similares al primero.
Modifique su propiedad para usar un tipo de envoltura
public sealed class XmlAnything<T> : IXmlSerializable
{
public XmlAnything() {}
public XmlAnything(T t) { this.Value = t;}
public T Value {get; set;}
public void WriteXml (XmlWriter writer)
{
if (Value == null)
{
writer.WriteAttributeString("type", "null");
return;
}
Type type = this.Value.GetType();
XmlSerializer serializer = new XmlSerializer(type);
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
serializer.Serialize(writer, this.Value);
}
public void ReadXml(XmlReader reader)
{
if(!reader.HasAttributes)
throw new FormatException("expected a type attribute!");
string type = reader.GetAttribute("type");
reader.Read();
if (type == "null")
return;
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
reader.ReadEndElement();
}
public XmlSchema GetSchema() { return(null); }
}
Usar esto implicaría algo como (en el proyecto P):
public namespace P
{
public interface IFoo {}
public class RealFoo : IFoo { public int X; }
public class OtherFoo : IFoo { public double X; }
public class Flibble
{
public XmlAnything<IFoo> Foo;
}
public static void Main(string[] args)
{
var x = new Flibble();
x.Foo = new XmlAnything<IFoo>(new RealFoo());
var s = new XmlSerializer(typeof(Flibble));
var sw = new StringWriter();
s.Serialize(sw, x);
Console.WriteLine(sw);
}
}
que te da:
<?xml version="1.0" encoding="utf-16"?>
<MainClass
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Foo type="P.RealFoo, P, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
<RealFoo>
<X>0</X>
</RealFoo>
</Foo>
</MainClass>
Obviamente, esto es más engorroso para los usuarios de la clase, aunque evita mucha placa de caldera.
Un medio feliz puede ser fusionar la idea de XmlAnything en la propiedad de 'respaldo' de la primera técnica. De esta manera, la mayor parte del trabajo pesado está hecho por usted, pero los consumidores de la clase no sufren ningún impacto más allá de la confusión con la introspección.