Mam bardzo dziwny problem podczas pracy z .NET XmlSerializer
.
Weź następujące przykładowe zajęcia:
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, istnieją trzy różne metody rozwiązania problemu InvalidOperationException
spowodowanego brakiem wiedzy serializatora o typach pochodnych Payment
.
1. Dodanie XmlInclude
do Payment
definicji klasy:
Nie jest to możliwe, ponieważ wszystkie klasy są uwzględnione jako odwołania zewnętrzne, nad którymi nie mam kontroli.
2. Przekazywanie typów typów pochodnych podczas tworzenia XmlSerializer
instancji
Nie działa.
3. Definiowanie XmlAttributeOverrides
właściwości docelowej w celu zastąpienia domyślnej serializacji właściwości (jak wyjaśniono w tym poście SO )
Również nie działa ( XmlAttributeOverrides
następuje inicjalizacja).
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
Następnie zostanie użyty odpowiedni konstruktor.
UWAGA: przez nie działa, mam na myśli, że InvalidOperationException
( BankPayment
nie oczekiwano ... ) jest rzucany.
Czy ktoś może rzucić trochę światła na ten temat? Jak można by zająć się i dalej debugować problem?
źródło
Właśnie rozwiązałem problem. Po dłuższym kopaniu znalazłem ten wpis SO, który obejmuje dokładnie tę samą sytuację. To doprowadziło mnie na właściwy tor.
Zasadniczo
XmlSerializer
należy znać domyślną przestrzeń nazw, jeśli klasy pochodne są uwzględnione jako dodatkowe typy. Dokładny powód, dla którego musi się to zdarzyć, jest nadal nieznany, ale nadal serializacja działa teraz.źródło
Na tej podstawie mogłem to rozwiązać, zmieniając konstruktora, z
XmlSerializer
którego korzystałem, zamiast zmieniać klasy.Zamiast używać czegoś takiego (sugerowanego w innych odpowiedziach):
[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(); }
Ja to zrobiłem:
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(); }
źródło
Zgadzam się z bizl
[XmlInclude(typeof(ParentOfTheItem))] [Serializable] public abstract class WarningsType{ }
także jeśli potrzebujesz zastosować tę klasę dołączoną do obiektu, możesz to zrobić w ten sposób
[System.Xml.Serialization.XmlElementAttribute("Warnings", typeof(WarningsType))] public object[] Items { get { return this.itemsField; } set { this.itemsField = value; } }
źródło
Po prostu zrób to w Base, w ten sposób każde dziecko może zostać zserializowane, mniej czystszy kod.
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 } }
W ten sposób możesz wywołać Serialize na klasie podrzędnej bez względu na okoliczności i nadal być w stanie zrobić to, czego potrzebujesz, zanim obiekt Serializuje.
źródło