У меня очень странная проблема при работе с .NET XmlSerializer
.
Возьмем следующие примеры классов:
public class Order
{
public PaymentCollection Payments { get; set; }
//everything else is serializable (including other collections of non-abstract types)
}
public class PaymentCollection : Collection<Payment>
{
}
public abstract class Payment
{
//abstract methods
}
public class BankPayment : Payment
{
//method implementations
}
AFAIK, есть три разных метода решения проблемы InvalidOperationException
, вызванной тем, что сериализатор не знает о производных типах Payment
.
1. Добавление XmlInclude
к Payment
определению класса:
Это невозможно из-за того, что все классы включены как внешние ссылки, над которыми я не могу контролировать.
2. Передача типов производных типов при создании XmlSerializer
экземпляра.
Не работает.
3. Определение XmlAttributeOverrides
целевого свойства для отмены сериализации свойства по умолчанию (как описано в этом сообщении SO )
Тоже не работает ( XmlAttributeOverrides
следует инициализация).
Type bankPayment = typeof(BankPayment);
XmlAttributes attributes = new XmlAttributes();
attributes.XmlElements.Add(new XmlElementAttribute(bankPayment.Name, bankPayment));
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Order), "Payments", attributes);
XmlSerializer
Затем будет использован соответствующий конструктор.
ПРИМЕЧАНИЕ: под не работает, я имею в виду, что InvalidOperationException
( BankPayment
не ожидалось ... ) выбрасывается.
Может ли кто-нибудь пролить свет на эту тему? Как можно дальше отлаживать проблему?
источник
Просто решил вопрос. Покопавшись еще немного, я нашел этот пост SO, который описывает ту же ситуацию. Это привело меня в правильное русло.
По сути,
XmlSerializer
необходимо знать пространство имен по умолчанию, если производные классы включены как дополнительные типы. Точная причина, по которой это должно произойти, до сих пор неизвестна, но, тем не менее, сериализация сейчас работает.источник
Исходя из этого, я смог решить эту проблему, изменив конструктор, который
XmlSerializer
я использовал вместо изменения классов.Вместо использования чего-то вроде этого (предложено в других ответах):
[XmlInclude(typeof(Derived))] public class Base {} public class Derived : Base {} public void Serialize() { TextWriter writer = new StreamWriter(SchedulePath); XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>)); xmlSerializer.Serialize(writer, data); writer.Close(); }
Я сделал это:
public class Base {} public class Derived : Base {} public void Serialize() { TextWriter writer = new StreamWriter(SchedulePath); XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>), new[] { typeof(Derived) }); xmlSerializer.Serialize(writer, data); writer.Close(); }
источник
Просто сделайте это в базе, таким образом любой дочерний элемент может быть сериализован, меньше кода очистки кода.
public abstract class XmlBaseClass { public virtual string Serialize() { this.SerializeValidation(); XmlSerializerNamespaces XmlNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }); XmlWriterSettings XmlSettings = new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }; StringWriter StringWriter = new StringWriter(); XmlSerializer Serializer = new XmlSerializer(this.GetType()); XmlWriter XmlWriter = XmlWriter.Create(StringWriter, XmlSettings); Serializer.Serialize(XmlWriter, this, XmlNamespaces); StringWriter.Flush(); StringWriter.Close(); return StringWriter.ToString(); } protected virtual void SerializeValidation() {} } [XmlRoot(ElementName = "MyRoot", Namespace = "MyNamespace")] public class XmlChildClass : XmlBaseClass { protected override void SerializeValidation() { //Add custom validation logic here or anything else you need to do } }
Таким образом, вы можете вызывать Serialize для дочернего класса независимо от обстоятельств и по-прежнему иметь возможность делать то, что вам нужно, до сериализации объекта.
источник
Я согласен с bizl
[XmlInclude(typeof(ParentOfTheItem))] [Serializable] public abstract class WarningsType{ }
также, если вам нужно применить этот включенный класс к объекту, вы можете сделать это
[System.Xml.Serialization.XmlElementAttribute("Warnings", typeof(WarningsType))] public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } }
источник