Это просто внутреннее ограничение декларативной сериализации, когда информация о типе не встроена в выходные данные.
При попытке преобразовать <Flibble Foo="10" />
обратно в
public class Flibble { public object Foo { get; set; } }
Как сериализатор узнает, должно ли это быть int, string, double (или что-то еще) ...
Чтобы сделать эту работу, у вас есть несколько вариантов, но если вы действительно не знаете, до времени выполнения, самый простой способ сделать это, вероятно, будет использовать 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>
This is obviously more cumbersome for users of the class though avoids much boiler plate.
A happy medium may be merging the XmlAnything idea into the 'backing' property of the first technique. In this way most of the grunt work is done for you but consumers of the class suffer no impact beyond confusion with introspection.