I'm attempting to deserialize a custom class via the XmlSerializer and having a few problems, in the fact that I don't know the type that I'm going to be deserializing (it's pluggable) and I'm having difficulty determining it.
I found this post which looks similar but can't quite get it to work with my approach because I need to deserialize an interface which is XmlSerializable.
What I've currently got is of the form. Note that I expect and need to be able to handle both class A and class B to be implemented via a plugin. So if I can avoid using the IXmlSerializable (which I don't think I can) then that would be great.
The ReadXml for A is what I'm stuck on. However if there are other changes that I can make to improve the system then I'm happy to do so.
public class A : IXmlSerializable
{
public IB MyB { get; set;}
public void ReadXml(System.Xml.XmlReader reader)
{
// deserialize other member attributes
SeekElement(reader, "MyB");
string typeName = reader.GetAttribute("Type");
// Somehow need to the type based on the typename. From potentially
//an external assembly. Is it possible to use the extra types passed
//into an XMlSerializer Constructor???
Type bType = ???
// Somehow then need to deserialize B's Members
// Deserialize X
// Deserialize Y
}
public void WriteXml(System.Xml.XmlWriter writer)
{
// serialize other members as attributes
writer.WriteStartElement("MyB");
writer.WriteAttributeString("Type", this.MyB.GetType().ToString());
this.MyB.WriteXml(writer);
writer.WriteEndElement();
}
private void SeekElement(XmlReader reader, string elementName)
{
ReaderToNextNode(reader);
while (reader.Name != elementName)
{
ReaderToNextNode(reader);
}
}
private void ReaderToNextNode(XmlReader reader)
{
reader.Read();
while (reader.NodeType == XmlNodeType.Whitespace)
{
reader.Read();
}
}
}
public interface IB : IXmlSerializable
{
}
public class B : IB
{
public void ReadXml(XmlReader reader)
{
this.X = Convert.ToDouble(reader.GetAttribute("x"));
this.Y = Convert.ToDouble(reader.GetAttribute("y"));
}
public void WriteXml(XmlWriter writer)
{
writer.WriteAttributeString("x", this.X.ToString());
writer.WriteAttributeString("y", this.Y.ToString());
}
}
NOTE : Updated as I realised B was supposed to use interface IB. Sorry for slightly wrong question.
To create an instance from a string, use one of the overloads of Activator.CreateInstance. To just get a type with that name, use Type.GetType.
I don't think you need to implement IXmlSerializable...
Since you don't know the actual types before runtime, you can dynamically add attribute overrides to the XmlSerializer. You just need to know the list of types that inherit from A. For instance, if you use A as a property of another class :
public class SomeClass
{
public A SomeProperty { get; set; }
}
You can dynamically apply XmlElementAttributes for each derived type to that property :
XmlAttributes attr = new XmlAttributes();
var candidateTypes = from t in AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes())
where typeof(A).IsAssignableFrom(t) && !t.IsAbstract
select t;
foreach(Type t in candidateTypes)
{
attr.XmlElements.Add(new XmlElementAttribute(t.Name, t));
}
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(SomeClass), "SomeProperty", attr);
XmlSerializer xs = new XmlSerializer(typeof(SomeClass), overrides);
...
This is just a very basic example, but it shows how to apply XML serialization attributes at runtime when you can't do it statically.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With