Expand|Select|Wrap|Line Numbers
- public interface IPoint
- {
- double X;
- double Y;
- double DistanceBetween(IPoint point);
- }
- public class Point2Dimensional : IPoint
- {
- public double X { get; protected set; }
- public double Y { get; protected set; }
- public virtual double DistanceBetween(IPoint point) { ... }
- }
- public class Point3Dimensional : Point2Dimensional
- {
- public double Z { get; protected set; }
- public override double DistanceBetween(IPoint point) { ... }
- }
Expand|Select|Wrap|Line Numbers
- public class PointHolder
- {
- public IPoint Point { get; set; }
- }
Firstly we want to make sure both our points can serialize / deserialize themselves. So we change our code slightly. I've ommitted the parameterless constructors, obviously you'll need to add those in.
1) Make the IPoint interface implement IXmlSerializable
Expand|Select|Wrap|Line Numbers
- public interface IPoint : IXmlSerializable
- {
- double X;
- double Y;
- double DistanceBetween(IPoint point);
- }
Expand|Select|Wrap|Line Numbers
- public class Point2Dimensional : IPoint
- {
- public double X { get; protected set; }
- public double Y { get; protected set; }
- public virtual double DistanceBetween(IPoint point) { ... }
- public System.Xml.Schema.XmlSchema GetSchema()
- {
- return null;
- }
- public void ReadXml(System.Xml.XmlReader reader)
- {
- this.X = Convert.ToDouble(reader.GetAttribute("X"));
- this.Y = Convert.ToDouble(reader.GetAttribute("Y"));
- }
- public void WriteXml(System.Xml.XmlWriter writer)
- {
- writer.WriteAttributeString("X", this.X.ToString());
- writer.WriteAttributeString("Y", this.Y.ToString());
- }
- }
Expand|Select|Wrap|Line Numbers
- public class PointHolder : IXmlSerializable
- {
- public IPoint Point { get; set; }
- public System.Xml.Schema.XmlSchema GetSchema()
- {
- return null;
- }
- public void ReadXml(System.Xml.XmlReader reader)
- {
- this.Point = reader.ReadXmlSerializableElement<IPoint>("Point");
- }
- public void WriteXml(System.Xml.XmlWriter writer)
- {
- writer.SaveXmlSerialiableElement("Point", this.Point);
- }
- }
Expand|Select|Wrap|Line Numbers
- public static void SaveXmlSerialiableElement<T>(this XmlWriter writer, String elementName, T element) where T : IXmlSerializable
- {
- writer.WriteStartElement(elementName);
- writer.WriteAttributeString("TYPE", element.GetType().ToString());
- element.WriteXml(writer);
- writer.WriteEndElement();
- }
<Point>
<Point3Dimensional TYPE="TestApp.Point3Dimensional" x="0", y="0", z="0">
</Point>
Not too difficult. Reading is where things get interesting. There's a couple of methods that I won't go into that you'll need, I'm sure you can figure them out. The code is here:
Expand|Select|Wrap|Line Numbers
- public static void ReadToElement(this XmlReader reader, String element)
- {
- reader.ReadToNextNode();
- while (reader.Name != element)
- {
- reader.ReadToNextNode();
- }
- }
- public static void ReadToNextNode(this XmlReader reader)
- {
- // Read the current item to throw it away
- reader.Read();
- // Keep on reading till we have read all the whitespace
- while (reader.NodeType == XmlNodeType.Whitespace)
- {
- reader.Read();
- }
- }
Expand|Select|Wrap|Line Numbers
- public static T ReadXmlSerializableElement<T>(this XmlReader reader, String elementName) where T : IXmlSerializable
- {
- reader.ReadToElement(elementName);
- Type elementType = Type.GetType(reader.GetAttribute("TYPE"));
- T element = (T)Activator.CreateInstance(elementType);
- element.ReadXml(reader);
- return element;
- }
This then means we can use XmlSerialization on our PointHolder regardless of the type of IPoint that it is storing within the Point property.