这是串行化的高级主题。 对于那些不知道XML序列化是什么的人,那么这可能还不适合您。 对于那些这样做的人,您可能会遇到序列化接口的问题,而使用标准XmlSerialization技术根本无法做到这一点。 本文将探讨如何做到这一点,尽管可能会有些长。 让我们解决以下问题:
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) { ... }
}
如果我们现在有一个具有IPoint的类。
public class PointHolder
{
public IPoint Point { get; set; }
}
现在,这意味着我们无法使用标准的序列化来反序列化PointHolder类,因为该序列化器还不够聪明,无法确定应反序列化的类型。
但是,可以使用一些自定义序列化手动进行操作。
首先,我们要确保我们的两点都能序列化/反序列化。 因此,我们稍微更改了代码。 我省略了无参数构造函数,显然您需要在其中添加它们。
1)使IPoint接口实现IXmlSerializable
public interface IPoint : IXmlSerializable
{
double X;
double Y;
double DistanceBetween(IPoint point);
}
2)像这样使Point2Dimensional和Point3Dimensional实现IXmlSerializable接口。
我刚刚完成了2Dimensional类,显然另一个只是将Z添加到要序列化/反序列化的事物列表中。
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());
}
}
现在,我们还需要使PointHolder实现IXmlSerializable。
这使用了一些扩展方法,这些方法可以完成我们将在一分钟内看到的艰苦工作。
同样,您也将需要无参数构造函数。
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);
}
}
我们在这里所做的基本上是传递一个元素名称,然后保存或分配我们的Point属性。
现在,让我们看一下在静态类中某个地方定义的扩展方法。
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();
}
这里发生了什么?
好吧,我们用给定的名称编写一个新元素,然后保存该元素的完整类型和名称空间,在元素本身上调用WriteXml,然后编写结束标记。
因此,XML如下所示:
<点>
<Point3Dimensional TYPE =“ TestApp.Point3Dimensional” x =“ 0”,y =“ 0”,z =“ 0”>
</ Point>
不太困难。 阅读是使事情变得有趣的地方。 有几种我不需要的方法,我敢肯定您可以弄清楚。 代码在这里:
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();
}
}
但是,真正有用的部分是:
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;
}
这是首先确定感兴趣的元素。
然后,我们读出元素的类型并创建它的新实例。
然后,我们可以调用元素上的ReadXml以初始化其所有成员并返回它。
这意味着我们可以在PointHolder上使用XmlSerialization,而不管它存储在Point属性中的IPoint的类型如何。
From: https://bytes.com/topic/net/insights/871260-how-apply-xmlserialization-interface