Dies ist einfach eine inhärente Einschränkung der deklarativen Serialisierung, bei der Typinformationen nicht in die Ausgabe eingebettet sind.
Beim Versuch, <Flibble Foo="10" />wieder in umzuwandeln
public class Flibble { public object Foo { get; set; } }
Woher weiß der Serializer, ob es sich um ein Int, einen String, ein Double (oder etwas anderes) handeln soll?
Damit dies funktioniert, haben Sie mehrere Möglichkeiten. Wenn Sie es jedoch erst zur Laufzeit wirklich wissen, ist es wahrscheinlich am einfachsten, die XmlAttributeOverrides zu verwenden .
Leider funktioniert dies nur mit Basisklassen, nicht mit Schnittstellen. Das Beste, was Sie dort tun können, ist, die Eigenschaft zu ignorieren, die für Ihre Anforderungen nicht ausreicht.
Wenn Sie wirklich bei Schnittstellen bleiben müssen, haben Sie drei echte Optionen:
Verstecke es und kümmere dich in einer anderen Eigenschaft darum
Hässliche, unangenehme Kesselplatte und viel Wiederholung, aber die meisten Verbraucher der Klasse werden sich nicht mit dem Problem befassen müssen:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { /* code here to convert any type in Foo to string */ }
set { /* code to parse out serialized value and make Foo an instance of the proper type*/ }
}
Dies wird wahrscheinlich zu einem Alptraum für die Instandhaltung ...
Implementieren Sie IXmlSerializable
Ähnlich wie bei der ersten Option übernehmen Sie jedoch die volle Kontrolle über die Dinge
- Vorteile
- Sie haben keine bösen "falschen" Eigenschaften herumhängen.
- Sie können direkt mit der XML-Struktur interagieren und so Flexibilität / Versionierung hinzufügen
- Nachteile
- Möglicherweise müssen Sie das Rad für alle anderen Eigenschaften der Klasse erneut implementieren
Probleme der Doppelarbeit sind ähnlich wie beim ersten.
Ändern Sie Ihre Eigenschaft, um einen Umhüllungstyp zu verwenden
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(); // consume the value
if (type == "null")
return;// leave T at default value
XmlSerializer serializer = new XmlSerializer(Type.GetType(type));
this.Value = (T)serializer.Deserialize(reader);
reader.ReadEndElement();
}
public XmlSchema GetSchema() { return(null); }
}
Dies zu verwenden würde ungefähr Folgendes beinhalten (in Projekt 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);
}
}
was dir gibt:
<?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>
Dies ist für Benutzer der Klasse offensichtlich umständlicher, vermeidet jedoch viel Kesselplatte.
Ein glückliches Medium kann darin bestehen, die XmlAnything-Idee in die Backing-Eigenschaft der ersten Technik zu integrieren. Auf diese Weise wird der größte Teil der Grunzarbeit für Sie erledigt, aber die Verbraucher der Klasse leiden nicht unter Auswirkungen, die über die Verwechslung mit der Selbstbeobachtung hinausgehen.