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

2023-09-03 05:10:41 作者:采菇凉的小蘑菇

我有一组数据传输对象(例如很多reqest的,响应消息类,比如MainRequest,MainResponse,ShutDownRequest,ShutDownResponse)的当新类滚滚而来的项目的发展的。这些类必须是(DE)从和各种XML格式,具有不同的公共的XSD 连载。 新XML格式来作为项目的进展了。的

I have a set of data transfer objects (e.g. a lot of reqest, response message classes, like MainRequest, MainResponse, ShutDownRequest, ShutDownResponse) Where new classes keep coming as the project evolves. These classes must be (de)serialized from and to various XML formats with different public XSDs. New XML formats come as the project evolves too.

我这里的问题是,我怎么会设计在我的类和接口的这两个要求的,特别是当我应该把实际的(de)serilization逻辑。我应该写一个静态服务,可以采取不同的DTO实例的知道如何序列每个人?当新的类来我要摸每个FormatXSeriaizer并添加新的覆盖。随着新的格式来我只需要编写新的FormatXSerializer类。

My question here is how I would design my classes and interfaces around these two requirement, especially where I should put the actual (de)serilization logic. Should I write a static service that can take the various DTO instances an knows how to serialize each of them? When new classes come I have to touch each FormatXSeriaizer and add new overrides. As new formats come I just have to write new FormatXSerializer classes.

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类。

or should the DTOs themself know how to do it. So I have it all in one place - for each DTO class. As new DTO classes come, they just have to implement the serialization for the various formats. As new formats come, I have to touch each DTO class.

myMainRequestInstace.Serialize(FormatTypeEnum type, XmlWriter writer);

或者是有一个完整的不同的方法?我应该引入一个共同的inteface进行序列化,并有控制反转,这样我就可以在运行时加载新的格式序列化?

Or is there a complete different approach? Should I introduce a common inteface for serialization and have some inversion of control, so I could load new format serializers at runtime?

什么样的​​设计模式能引导我到这里来?

What design pattern can guide me here?

什么开源$ C ​​$ C在.NET世界可能我学习上看到这个问题的不同方法?

What open source code in the .NET world could I study to see different approaches on this subject?

编辑: 我知道在现有的框架内的一般序列化技术。我的问题是实现一流的设计较侧重尊重了两个要求:多个XML格式和多种的DTO(消息类型)滚滚而来随着项目的发展

I know about the general serialization techniques existing in the framework. My question is more geared towards class design that respects the two requirements: multiple xml formats and multiple DTOs (message types) that keep coming as the project evolves.

推荐答案

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

The best approach would be something like this, this is my favourite approach:


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来了,好了,旧习难改。

This is how I enforce a tighter grain of control over the serialization of binary data and bump up the version. Now, those of you will point out that there is already a feature available to do this, but coming from .NET 1.1, well, old habits die hard.

请注意如何code样品上面我用两种不同的方法在 HandleVersionOnePtZero HandleVersionOnePtOne 处理不同版本的序列化流的。通过做这种方式,我有一个更大程度的灵活性,说什么如果需要更改的字段 someField ?另外,还要注意如何使用 _fVersion 字段是序列化的程序首先做的第一件事,然后检查了现场的版本,并决定使用哪一个。

Notice how in the code sample above I used two different methods HandleVersionOnePtZero and HandleVersionOnePtOne to handle the different versions of the serialized stream. By doing it this way, I have a greater degree of flexibility, say what if the field someField needs to be changed? Also, notice how the _fVersion field is the first thing the serializable routine does firstly, then checks the version of the field and decide which one to use.

这个唯一的一点,就是如果你改变了命名空间,那么你将很难反序列化的数据,但可以使用SerializationBinder类为例:

The only thing about this, is if you change the namespace, then you will have difficulty deserializing the data, but you can use the SerializationBinder class as an example:


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

希望这有助于

Hope this helps