65.9K
CodeProject 正在变化。 阅读更多。
Home

自定义序列化 - 第二部分

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.86/5 (10投票s)

2008年3月6日

CPOL

5分钟阅读

viewsIcon

79371

downloadIcon

635

.NET 中的自定义序列化

引言

上一篇文章中,我讨论了 .NET 如何在 Web 服务场景中进行序列化,将对象转换为 XML,反之亦然。在本文中,我将更详细地讨论 .NET 序列化类以及如何使用它们。

自定义序列化与反序列化

自定义序列化是指我们确定对象应如何转换为字节流的过程。.NET 提供了不同的自定义序列化方法。

使用特性

属性应用于类级别,并标记该类的实例可以被序列化。类的属性、public 字段将根据两个属性进行序列化。它们是:

  • [XmlAttribute]:表示 public 字段或属性将被序列化为 XML 节点中的一个属性
  • [XmlElement]:表示 public 字段或属性将被序列化为 XML 节点中的一个元素

如果您不希望序列化某个字段,可以将其标记为 [NonSerialized]。以下代码片段展示了 Customer 类,它被标记为 Serializable,并且 public 字段被序列化为属性和元素。

[Serializable]
    public class Customer
    {
        [XmlAttribute("ID")]
        public string Id;

        [XmlElement("Name")]
        public string Name;

        [XmlElement("Hire")]
        public DateTime HireDate;

        [XmlElement("Rate")]
        public decimal RatePerHour;

        [XmlAttribute("Dob")]
        public DateTime Dob;

        public Customer()
        {
        }
    }

定义实体类后,您可以使用 System.Xml.Serialization namespace 中的 XmlSerializer 类来序列化和反序列化实例。以下代码片段序列化 Customer 实例,将其转换为 string 并在文本框中显示结果。请参考示例应用程序中的 frmCustomer

XmlSerializer serializer = new XmlSerializer(typeof(Customer));
StringWriter writer = new StringWriter();
serializer.Serialize(writer, customer);
textBoxResult.Text = writer.ToString();

反序列化是反向过程,代码片段如下。

StringReader reader = new StringReader(textBoxResult.Text);
XmlSerializer serializer = new XmlSerializer(typeof(Customer));
Customer customer = (Customer) serializer.Deserialize(reader);

优点和缺点

这种方法具有以下优点:

  • 可以灵活地指定属性是否应被序列化
  • 可以序列化为属性或元素
  • 通过属性进行控制

这种方法具有以下缺点:

  • 无法在运行时控制格式
  • 格式的更改需要代码更改,因此需要重新编译、重新部署代码

使用 IXmlSerializable 接口

自定义序列化与反序列化的第二种技术是通过实现 IXmlSerializable 接口。Product 类实现了 IXmlSerializableReadXml() 方法将在反序列化过程中被调用,WriteXml() 方法将在序列化对象时被调用。

实体类不需要类级别或属性/字段级别的任何属性。运行时将自动考虑在序列化与反序列化过程中所有的 public 属性和字段。

WriteXml() 方法中,我们可以决定如何序列化属性。当您使用 XmlWriter 写入属性时,XML 文档将由属性构成。以下代码片段将属性作为属性写入。

public void WriteXml(XmlWriter writer)
{
    writer.WriteAttributeString("Id", Id);
    writer.WriteAttributeString("Name", Name);
    writer.WriteAttributeString("Quantity", Quantity.ToString());
    writer.WriteAttributeString("Rate", Rate.ToString());
}

ReadXml() 方法中,您需要按照创建 XML 文档的相同顺序执行从 XmlReader 读取的操作。

public void ReadXml(XmlReader reader)
{
    Id = reader.GetAttribute("Id");
    Name = reader.GetAttribute("Name");
    Quantity = int.Parse(reader.GetAttribute("Quantity"));
    Rate = decimal.Parse(reader.GetAttribute("Rate")) ;
}

优点和缺点

这种方法具有以下优点:

  • 以 XML 格式序列化,序列化为属性或元素。有利于互操作性。
  • 对序列化和反序列化有更好的灵活性和控制力。我们可以包含一些自定义逻辑,并决定是否序列化/反序列化某个属性。

这种方法具有以下缺点:

  • 每个实体都需要 ReadXml() WriteXml() 实现。在拥有更多实体的系统中,在所有实体中实现 ReadXml() WriteXml() 逻辑将花费大量时间。

使用 ISerializable 接口

还可以通过在类上实现 ISerializable 接口来实现自定义序列化。当序列化对象实例时,运行时将调用 GetObjectData() 方法。在此方法中,您需要添加需要序列化的属性和值。当您反序列化对象时,需要一个特殊的重载构造函数,在该构造函数中获取值并填充您的属性。数据将使用 Binary formatter 进行序列化,因此性能会比其他方法更好,但对象不可互操作。

这与 IXmlSerializable 接口非常相似,但区别在于您将处理 SerializationInfo 实例而不是 XmlReader XmlWriter 类。

使用 ISerializable 接口进行序列化

public void GetObjectData(SerializationInfo info, StreamingContext context)
{
    info.AddValue("Id", _id);
    info.AddValue("OrderDate", _orderDate);
    info.AddValue("Customer", customer);
    info.AddValue("Products", products);
}

使用 ISerializable 接口进行反序列化

public Order(SerializationInfo info, StreamingContext context)
{
    _id = info.GetString("Id");
    _orderDate = info.GetDateTime("OrderDate");
    customer = (Customer)info.GetValue("Customer", typeof(Customer));
    products = (Product[])info.GetValue("Products", typeof(Product[]));
}

Order 类有一个对象图。它包含一个 customer 实例和一个 Product 实例数组。

调用序列化过程的代码如下:

MemoryStream memoryStream = new MemoryStream();
StreamWriter writer = new StreamWriter(memoryStream);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(memoryStream, order);
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] bytes = memoryStream.ToArray();

调用反序列化过程的代码如下:

MemoryStream memoryStream = new MemoryStream(bytes);
BinaryFormatter formatter = new BinaryFormatter();
Order order = (Order) formatter.Deserialize(memoryStream);

优点和缺点

这种方法具有以下优点:

  • 以二进制格式序列化,获得最大性能。
  • 对序列化和反序列化过程有更好的灵活性和控制力。我们可以包含一些自定义逻辑,并决定是否序列化/反序列化某个属性。

这种方法具有以下缺点:

  • 每个实体都需要 GetObjectData() 和专门的构造函数实现。在拥有更多实体的系统中,在所有类中实现这些方法将花费大量时间。

关于代码

该应用程序有三个窗体:

  1. 客户
  2. 产品
  3. Order

每个窗体都演示了一种不同的技术。窗体将序列化实例并在文本框中显示序列化数据,然后反序列化函数将创建实例并填充控件。您可以更改序列化数据的值,并观察反序列化过程拾取更改后的数据并填充控件。Order 窗体以二进制格式进行序列化和反序列化,这将在窗体中显示。

Customer 窗体使用 Customer 类。该窗体演示了基于属性的序列化。

CustomerForm.JPG

Product 窗体使用 Product 类。该窗体演示了使用 IXmlSerializable 接口的序列化。

ProductForm.jpg

Order 窗体使用 Order 类,该类内部包含 Customer Product[]。该窗体演示了使用 ISerializable 接口和二进制格式的序列化。

OrderForm.JPG

结论

.NET Framework 提供了许多选项来执行类的实例的自定义序列化和反序列化。根据灵活性、互操作性、代码可维护性、涉及的工作量等各种因素,我们可以选择适当的技术。

在下一篇文章中,我将讨论如何从头开始完全实现一个序列化引擎,并将互操作性和灵活性作为关键要求。

阅读愉快!编码愉快!

历史

  • 2008 年 3 月 6 日:初始发布
© . All rights reserved.