在.NET中序列化数据传输对象

时间:2010-01-23 00:12:26

标签: .net serialization class-design

我有一组数据传输对象(例如很多请求,响应消息类,如MainRequest,MainResponse,ShutDownRequest,ShutDownResponse)随着项目的发展,新类继续出现。这些类必须(使用不同的公共XSD 序列化为各种XML格式新的XML格式也随着项目的发展而变化。

我的问题是如何围绕这两个要求设计我的类和接口,特别是在我应该放置实际(de)serilization逻辑的地方。我应该编写一个可以接受各种DTO实例的静态服务,知道如何序列化它们吗?当新课程出现时,我必须触摸每个FormatXSeriaizer并添加新的覆盖。随着新格式的出现,我只需编写新的FormatXSerializer类。

FormatASerializer.Serialize(XmlWriter writer, MainResponse r);
FormatASerializer.Serialize(XmlWriter writer, ShutDownResponse r);
FormatBSerializer.Serialize(XmlWriter writer, MainResponse r);
FormatBSerializer.Serialize(XmlWriter writer, ShutDownResponse r);
或者DTO自己应该知道如何做到这一点。所以我把它放在一个地方 - 每个DTO课程。随着新的DTO课程的出现,他们只需要实现各种格式的序列化。随着新格式的出现,我必须触及每个DTO课程。

myMainRequestInstace.Serialize(FormatTypeEnum type, XmlWriter writer);

或者是否有完全不同的方法?我是否应该为序列化引入一个通用的接口并且有一些控制反转,所以我可以在运行时加载新格式的序列化器?

什么设计模式可以指导我?

我可以研究.NET世界中的哪些开源代码,以查看有关此主题的不同方法?

修改: 我知道框架中存在的一般序列化技术。我的问题更侧重于尊重这两个要求的类设计:多个xml格式和多个DTO(消息类型)随着项目的发展而不断发展。

4 个答案:

答案 0 :(得分:5)

最好的方法是这样的,这是我最喜欢的方法:

public class SomeClass : ISerializable{
   private float _fVersion;
   ....
   public float Version {
       get { return this._fVersion; }
   }

   private SomeClass(SerializationInfo info, StreamingContext context) {
      bool bOk = false;
      this._fVersion = info.GetSingle("versionID");
      if (this._fVersion == 1.0F) bOk = this.HandleVersionOnePtZero(info, context);
      if (this._fVersion == 1.1F) bOk = this.HandleVersionOnePtOne(info, context);

      if (!bOk) throw new SerializationException(string.Format("SomeClass: Could not handle this version {0}.", this._fVersion.ToString()));
   }
   public void GetObjectData(SerializationInfo info, StreamingContext context) {
      info.AddValue("versionID", this._fVersion);
      if (this._fVersion == 1.0F) {
         info.AddValue("someField", ...);
         info.AddValue("anotherField", ...);
      }
      if (this._fVersion == 1.1F) {
         info.AddValue("someField1", ...);
         info.AddValue("anotherField2", ...);
      }
   }
   private bool HandleVersionOnePtZero(SerializationInfo info, StreamingContext context) {
      bool rvOk = false;
      ... = info.GetValue("someField");
      ... = info.GetValue("anotherField");
   }

   private bool HandleVersionOnePtOne(SerializationInfo info, StreamingContext context) {
      bool rvOk = false;
      ... = info.GetValue("someField1");
      ... = info.GetValue("anotherField2");
   }

}

这就是我如何对二进制数据的序列化实施更严格的控制并提升版本。现在,你们中的那些人会指出已经有一个功能可以做到这一点,但是来自.NET 1.1,老习惯很难。

请注意,在上面的代码示例中,我使用了两种不同的方法HandleVersionOnePtZeroHandleVersionOnePtOne来处理序列化流的不同版本。通过这种方式,我有更大程度的灵活性,如果字段someField需要改变怎么办?另外,请注意_fVersion字段是可序列化例程首先执行的操作,然后检查字段的版本并决定使用哪个字段。

唯一的问题是,如果更改命名空间,那么反序列化数据会很困难,但您可以使用SerializationBinder类作为示例:

public class SomeClassBinder : System.Runtime.Serialization.SerializationBinder {
    public override Type BindToType(string assemblyName, string typeName) {
      Type typeToDeserialize = null;
      try {
         // For each assemblyName/typeName that you want to deserialize to
         // a different type, set typeToDeserialize to the desired type.
         string assemVer1 = System.Reflection.Assembly.GetExecutingAssembly().FullName;
         if (assemblyName.StartsWith("foobar")) {
             assemblyName = assemVer1;
             typeName = "fubar" + typeName.Substring(typeName.LastIndexOf("."), (typeName.Length - typeName.LastIndexOf(".")));
         }
         typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
      } catch (System.Exception ex1) {
          throw ex1;
      } finally {
    }
    return typeToDeserialize;
  }
}

it would be called like this:
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new SomeClassBinder();
SomeClass sc = bf.Deserialize(stream); // assume stream is declared and open

希望这有帮助

答案 1 :(得分:3)

在.NET中已经有一些用于执行xml序列化的内置机制。

基于属性的序列化 - overview。您所要做的就是使用属性标记类的成员,并使用XmlSerializer类来序列化/反序列化类型。


    [XmlRoot("myXmlClass")]
    public class MyXmlClass
    {
        [XmlAttribute("myAttribtue")]
        public string MyAttribute { get; set; }
        [XmlElement("myElement")]
        public string MyElement { get; set; }
    }

    var output = new MemoryStream();
    var serializer = new XmlSerializer(typeof(MyXmlClass));
    serializer.Serialize(output, new MyXmlClass { MyAttribute = "foo", MyElement = "bar" });

输出 -

<myXmlClass myAttribute="foo">
    <myElement>bar</myElement>
</myXmlClass>

或者您可以使所有xml可序列化类实现IXmlSerializable

修改 - 现在,由于我误解了你原来的问题,我将使用一种技术来修改它,你可以使用这种技术将同一个对象序列化为多种不同的xml格式。

既然您的数据传输对象可以序列化为至少一种基于xml的格式,您可以执行后期翻译步骤,以使用xslt transforms将其转换为您想要的格式。 Xslt是一种获取xml并使用xslt文件将其转换为任何内容的方法。在这种情况下,您将翻译成另一种xml格式。

以下是(假设您已经编写了xslt文件) -


    // output stream from before
    output.Seek(0, SeekOrigin.Begin);
    var reader = XmlReader.Create(output);
    // cache this for performance reasons
    XslCompiledTransform transform = new XslCompiledTransform();
    transform.Load("c:\myTransforms\commonToFormatA.xslt");
    var writer = XmlWriter.Create(Console.Out); // write final output to console.
    transform.Transform(reader, writer);

答案 2 :(得分:1)

部分考虑因素是XML格式的不同之处。特别是,它们都可以通过使用XML序列化的覆盖功能来完成吗?这允许您提供一系列条目覆盖功能,如元素或属性名称/是否序列化为元素或属性等。

这可以允许您使各种格式仅在传递给序列化过程的覆盖数组方面有所不同。这将使您了解如何确定要传递的数组。

答案 3 :(得分:0)

了解自定义XML序列化程序,他们应该能够完成您需要的所有工作。

http://geekswithblogs.net/marcel/archive/2006/05/19/78989.aspx