Jest to po prostu nieodłączne ograniczenie serializacji deklaratywnej, w której informacje o typie nie są osadzone w danych wyjściowych.
Podczas próby konwersji z <Flibble Foo="10" />
powrotem do
public class Flibble { public object Foo { get; set; } }
Skąd serializator wie, czy powinien to być int, string, double (lub coś innego) ...
Aby to zadziałało, masz kilka opcji, ale jeśli naprawdę nie wiesz do czasu wykonania, najłatwiejszym sposobem na to jest użycie XmlAttributeOverrides .
Niestety będzie to działać tylko z klasami bazowymi, a nie interfejsami. Najlepsze, co możesz zrobić, to zignorować właściwość, która nie jest wystarczająca dla Twoich potrzeb.
Jeśli naprawdę musisz pozostać przy interfejsach, masz trzy prawdziwe opcje:
Ukryj to i zajmij się nim w innej nieruchomości
Brzydka, nieprzyjemna płyta kotłowa i dużo powtórzeń, ale większość konsumentów tej klasy nie będzie musiała radzić sobie z problemem:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { }
set { }
}
To prawdopodobnie stanie się koszmarem związanym z konserwacją ...
Zaimplementuj IXmlSerializable
Podobnie jak w przypadku pierwszej opcji, w której przejmujesz pełną kontrolę nad rzeczami, ale
- Plusy
- Nie masz w pobliżu paskudnych „fałszywych” nieruchomości.
- możesz współdziałać bezpośrednio ze strukturą XML, dodając elastyczność / wersjonowanie
- Cons
- może się okazać, że będziesz musiał ponownie zaimplementować koło dla wszystkich innych właściwości w klasie
Kwestie powielania wysiłków są podobne do pierwszego.
Zmodyfikuj właściwość, aby użyć typu zawijania
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); }
}
Użycie tego wymagałoby czegoś takiego (w projekcie 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);
}
}
co daje:
<?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>
Jest to oczywiście bardziej uciążliwe dla użytkowników tej klasy, ale pozwala uniknąć zbytniej płyty kotłowej.
Wesołym medium może być scalanie pomysłu XmlAnything z właściwością „backing” pierwszej techniki. W ten sposób większość podstawowej pracy jest wykonywana za Ciebie, ale konsumenci tej klasy nie mają żadnego wpływu poza pomyłką z introspekcją.