Problem rozwiązany!
OK, więc w końcu tam dotarłem (trzeba przyznać, że z dużą pomocą stąd !).
Więc podsumuj:
Cele:
- Nie chciałem iść w dół trasy XmlInclude z powodu bólu głowy związanego z utrzymaniem.
- Po znalezieniu rozwiązania chciałem, aby można je było szybko wdrożyć w innych aplikacjach.
- Można stosować kolekcje typów abstrakcyjnych, a także indywidualne właściwości abstrakcyjne.
- Naprawdę nie chciałem zawracać sobie głowy koniecznością robienia „specjalnych” rzeczy na konkretnych zajęciach.
Zidentyfikowane problemy / punkty, na które należy zwrócić uwagę:
- XmlSerializer robi całkiem fajną refleksję, ale jest bardzo ograniczony, jeśli chodzi o typy abstrakcyjne (tj. Będzie działać tylko z instancjami samego typu abstrakcyjnego, a nie z podklasami).
- Dekoratory atrybutów Xml definiują sposób, w jaki XmlSerializer traktuje znalezione właściwości. Można również określić typ fizyczny, ale tworzy to ścisłe powiązanie między klasą a serializatorem (niedobre).
- Możemy zaimplementować własny XmlSerializer, tworząc klasę, która implementuje IXmlSerializable .
Rozwiązanie
Stworzyłem klasę ogólną, w której określasz typ ogólny jako typ abstrakcyjny, z którym będziesz pracować. Daje to klasie możliwość „tłumaczenia” między typem abstrakcyjnym a typem konkretnym, ponieważ możemy na stałe zakodować rzutowanie (tj. Możemy uzyskać więcej informacji niż XmlSerializer).
Następnie zaimplementowałem interfejs IXmlSerializable , jest to dość proste, ale podczas serializacji musimy upewnić się, że zapisujemy typ konkretnej klasy do XML, abyśmy mogli odrzucić go z powrotem podczas deserializacji. Należy również zauważyć, że musi być w pełni kwalifikowany, ponieważ zespoły, w których znajdują się dwie klasy, mogą się różnić. Jest oczywiście trochę sprawdzania typu i rzeczy, które muszą się tutaj wydarzyć.
Ponieważ XmlSerializer nie może rzutować, musimy dostarczyć kod, aby to zrobić, więc niejawny operator jest następnie przeciążany (nawet nie wiedziałem, że możesz to zrobić!).
Oto kod AbstractXmlSerializer:
using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;
namespace Utility.Xml
{
public class AbstractXmlSerializer<AbstractType> : IXmlSerializable
{
public static implicit operator AbstractType(AbstractXmlSerializer<AbstractType> o)
{
return o.Data;
}
public static implicit operator AbstractXmlSerializer<AbstractType>(AbstractType o)
{
return o == null ? null : new AbstractXmlSerializer<AbstractType>(o);
}
private AbstractType _data;
public AbstractType Data
{
get { return _data; }
set { _data = value; }
}
public AbstractXmlSerializer()
{
}
public AbstractXmlSerializer(AbstractType data)
{
_data = data;
}
#region IXmlSerializable Members
public System.Xml.Schema.XmlSchema GetSchema()
{
return null;
}
public void ReadXml(System.Xml.XmlReader reader)
{
string typeAttrib = reader.GetAttribute("type");
if (typeAttrib == null)
throw new ArgumentNullException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because no 'type' attribute was specified in the XML.");
Type type = Type.GetType(typeAttrib);
if (type == null)
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the type specified in the XML was not found.");
if (!type.IsSubclassOf(typeof(AbstractType)))
throw new InvalidCastException("Unable to Read Xml Data for Abstract Type '" + typeof(AbstractType).Name +
"' because the Type specified in the XML differs ('" + type.Name + "').");
reader.ReadStartElement();
this.Data = (AbstractType)new
XmlSerializer(type).Deserialize(reader);
reader.ReadEndElement();
}
public void WriteXml(System.Xml.XmlWriter writer)
{
Type type = _data.GetType();
writer.WriteAttributeString("type", type.AssemblyQualifiedName);
new XmlSerializer(type).Serialize(writer, _data);
}
#endregion
}
}
Jak więc od tego momentu mamy powiedzieć XmlSerializer, aby współpracował z naszym serializatorem, a nie z domyślnym? Musimy przekazać nasz typ w ramach właściwości typu atrybutów XML, na przykład:
[XmlRoot("ClassWithAbstractCollection")]
public class ClassWithAbstractCollection
{
private List<AbstractType> _list;
[XmlArray("ListItems")]
[XmlArrayItem("ListItem", Type = typeof(AbstractXmlSerializer<AbstractType>))]
public List<AbstractType> List
{
get { return _list; }
set { _list = value; }
}
private AbstractType _prop;
[XmlElement("MyProperty", Type=typeof(AbstractXmlSerializer<AbstractType>))]
public AbstractType MyProperty
{
get { return _prop; }
set { _prop = value; }
}
public ClassWithAbstractCollection()
{
_list = new List<AbstractType>();
}
}
Tutaj możesz zobaczyć, mamy kolekcję i pojedynczą właściwość, które są ujawniane, a wszystko, co musimy zrobić, to dodać parametr o nazwie typu do deklaracji Xml, łatwe! :RE
UWAGA: Jeśli użyjesz tego kodu, byłbym wdzięczny za wiadomość. Pomoże również przyciągnąć więcej osób do społeczności :)
Teraz, ale nie jestem pewien, co zrobić z odpowiedziami tutaj, ponieważ wszyscy mieli swoje plusy i minusy. Zmodyfikuję te, które uważam za przydatne (bez obrazy dla tych, które nie były) i zakończę to, gdy będę miał przedstawiciela :)
Ciekawy problem i dobra zabawa do rozwiązania! :)