Це просто невід'ємне обмеження декларативної серіалізації, коли інформація про тип не вбудована у вихідні дані.
На спробу перетворити <Flibble Foo="10" />назад на
public class Flibble { public object Foo { get; set; } }
Звідки серіалізатор знає, чи це int, рядок, дубль (чи щось інше) ...
Щоб зробити цю роботу, у вас є кілька варіантів, але якщо ви справді не знаєте до часу виконання, найпростіший спосіб зробити це, швидше за все, буде використовувати XmlAttributeOverrides .
На жаль, це буде працювати лише з базовими класами, а не з інтерфейсами. Найкраще, що ви можете там зробити, - це ігнорувати властивість, яка недостатня для ваших потреб.
Якщо ви дійсно повинні залишатися з інтерфейсами, у вас є три реальні варіанти:
Сховайте це і впорайтеся з цим в іншому майні
Потворна, неприємна плита котла і багато повторень, але більшості споживачів класу не доведеться стикатися з проблемою:
[XmlIgnore()]
public object Foo { get; set; }
[XmlElement("Foo")]
[EditorVisibile(EditorVisibility.Advanced)]
public string FooSerialized
{
get { }
set { }
}
Це, мабуть, стане кошмаром для обслуговування ...
Впровадити IXmlSerializable
Подібно до першого варіанту, ви берете повний контроль над речами, але
- Плюси
- У вас немає неприємних "фальшивих" властивостей, що тусуються навколо.
- Ви можете взаємодіяти безпосередньо зі структурою xml, додаючи гнучкість / керування версіями
- Мінуси
- можливо, вам доведеться повторно застосувати колесо для всіх інших властивостей класу
Питання дублювання зусиль подібні до першого.
Змініть своє властивість, щоб використовувати тип обтікання
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); }
}
Використання цього залучило б щось на зразок (у проекті 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);
}
}
що дає вам:
<?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>
Очевидно, це є більш громіздким для користувачів класу, хоча уникає великої кількості котлових плит.
Щасливим носієм може бути злиття ідеї XmlAnything у властивість «підкладки» першої техніки. Таким чином, більша частина бурчання робиться за вас, але споживачі класу не зазнають ніякого впливу, окрім плутанини з самоаналізом.