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

自定义 .NET 序列化 - 第三部分

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.67/5 (3投票s)

2008年3月26日

CPOL

4分钟阅读

viewsIcon

30820

downloadIcon

354

本文将概述如何实现自己的序列化引擎。

引言

在我上一篇文章中,我介绍了 .NET 中可用的各种序列化技术。在本文中,我将概述如何实现自己的序列化引擎。

背景

本文是之前文章的延续 - 自定义序列化 - 第二部分

序列化引擎

当 .NET 默认提供序列化时,我们为什么需要开发自定义解决方案?嗯,这样做可以实现一些功能,它们是:

  • 能够动态控制要序列化或不序列化的属性。这将帮助我们仅序列化用户根据授权角色和规则允许查看的属性。
  • 能够动态控制序列化数据的格式。例如,动态地将对象序列化为 XML 元素或 XML 属性。
  • 由于格式由我们控制,并且可以与其他平台共享相同的格式,因此在企业内部与其他平台进行互操作要容易得多。

现在我们已经确定了自定义序列化引擎的“为什么”和“好处”,我们将研究构建它的步骤。序列化引擎会将对象序列化为 XML 字符串,并将 XML 字符串反序列化回对象。该引擎将能够为任何对象执行此操作。该引擎通过获取属性列表并将它们根据配置文件转换为 XML 字符串来实现。为了方便起见,我将其称为映射文件。

映射文件将帮助引擎将对象转换为 XML,反之亦然。映射文件将包含以下信息来实现这一点:

  • 对象信息,例如 - 类名、类型、程序集、架构文件
  • 属性列表
  • 属性数据类型
  • XML 文档中每个属性的 XPath

下面是 Customer 对象的映射文件

CustomSerialization-Part3-MappingFile.JPG

下面是 Customer 对象的架构文件

CustomSerialization-Part3-SchemaFile.JPG

架构文件将定义 Customer 对象序列化为 XML 时的格式。在此示例中,Customer 对象的属性定义为 XML 元素。我们还可以将映射文件与每个属性 XPath 的架构文件关联起来。

序列化逻辑

现在,我们已经完成了基础工作,可以开始实现自定义序列化引擎了。序列化逻辑如下:

  1. 接受对象和映射文件名作为参数
  2. 加载映射 XML 文件
  3. 根据映射文件中的 schemaFile 属性值,从 XSD 文件生成一个空 XML
  4. 循环遍历“serializer/object/properties”定义的​​所有属性
    • 对于每个节点,获取名称并从实例的属性 getter 获取值
    • 根据 XPath 在 XML 中设置值
    • 对映射文件中的每个属性执行步骤 4 a、b
  5. XML 文档现在包含传入实例的 XML 表示形式

以下代码片段显示了序列化逻辑

public string Serialize(object instance, string mappingFile)
{
    Mapper mapper = Mapper.GetMappingFile(mappingFile);
    string xml = Serializer.GenerateXml(mapper.DataObject.SchemaFile);

    XmlDocument document = new XmlDocument();
    document.LoadXml(xml);

    Type type = instance.GetType();
    foreach (DataObjectProperty property in mapper.DataObject.Properties)
    {
        object oValue = type.InvokeMember(property.Name, BindingFlags.GetProperty | 
                        BindingFlags.Instance | BindingFlags.Public, null, instance, null);
        string value = oValue.ToString();

        XmlNode node = document.SelectSingleNode(property.Xpath);
        node.InnerXml = value;
    }

    return document.ChildNodes[1].OuterXml;
}

Mapper 是表示映射文件的强类型类。映射 XML 文件被形成一个强类型实例并加载到 Hashtable 中。这避免了对每个序列化逻辑重新加载 XML 文件,并提高了性能。

从 XSD 生成 XML

为了从给定的 XSD 文件生成空 XML,我使用了这个MSDN 文章中的代码。它接受一个 XSD 文件并返回一个空 XML。请参阅代码下载附带的 Microsoft.Xml.XMLGen 项目。

private static string GenerateXml(string schemaFile)
{
    StringBuilder builder = new StringBuilder();
    XmlWriter writer = XmlWriter.Create(builder);

    XmlSampleGenerator xmlGenerator = 
       new XmlSampleGenerator(schemaFile, null);

    xmlGenerator.WriteXml(writer);
    string xml = builder.ToString();
    return xml;
}

反序列化逻辑

反序列化逻辑将与序列化逻辑完全相反

  1. 接受 XML 字符串和映射文件名作为参数
  2. 加载映射 XML 文件
  3. 加载 XML 字符串
  4. 根据映射文件中的 schemaFile 属性值,加载程序集并创建实例
  5. 循环遍历“serializer/object/properties”定义的​​所有属性
    • 对于每个节点,获取名称,并为实例的属性 setter 设置值
    • 对映射文件中的每个属性执行步骤 5 a
  6. XML 文档现在包含传入实例的 XML 表示形式

以下代码片段显示了反序列化逻辑

public object Deserialize(string xmlData, string mappingFile)
{
    XmlDocument document = new XmlDocument();
    document.LoadXml(xmlData);
    
    Mapper mapper = Mapper.GetMappingFile(mappingFile);


    Assembly assembly = Assembly.Load(mapper.DataObject.Assembly);
    object instance = assembly.CreateInstance(mapper.DataObject.Type);
    Type type = instance.GetType();

    foreach (DataObjectProperty property in mapper.DataObject.Properties)
    {
        XmlNode node = document.SelectSingleNode(property.Xpath);

        if ( property.NodeType == 
        string value = node.InnerXml;

        Type memberType = type.GetProperty(property.Name, 
                          BindingFlags.Instance | BindingFlags.Public).PropertyType;

        object oFinalValue = Convert.ChangeType(value, memberType);
        type.InvokeMember(property.Name, BindingFlags.SetProperty | BindingFlags.Instance | 
                          BindingFlags.Public, null, instance, new object[] { oFinalValue });
    }

    return instance;
}

关于示例应用程序

示例应用程序包含一个客户表单,该表单接受数据以形成客户实例。序列化按钮将客户对象序列化为 XML 字符串并在结果文本框中显示。反序列化按钮从结果文本框中的 XML 中创建对象并在控件中显示。

调用序列化过程

CustomSerializer.Serializer serializer = new CustomSerializer.Serializer();
textBoxResult.Text = serializer.Serialize(customer, mappingFile);

调用反序列化过程

CustomSerializer.Serializer serializer = new CustomSerializer.Serializer();
Customer customer = (Customer)serializer.Deserialize(textBoxResult.Text, mappingFile);

应用程序快照

编码愉快,阅读愉快!

© . All rights reserved.