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

xuo3flqw  于 2023-03-13  发布在  .NET
关注(0)|答案(4)|浏览(127)

我有一组数据传输对象(例如,大量请求、响应消息类,如MainRequest、MainResponse、ShutDownRequest、ShutDownResponse)* 随着项目的发展,新类不断出现 。这些类必须与具有不同公共XSD的各种XML格式进行序列化(反序列化) 随着项目的发展,新的XML格式也会出现。*
我的问题是如何围绕这两个需求来设计类和接口,尤其是应该在哪里放置实际的序列化逻辑。我是否应该编写一个静态服务,它可以接受各种DTO示例并知道如何序列化它们中的每一个?当新类出现时,我必须接触每个FormatXSerializer并添加新的覆盖。当新格式出现时,我只需要编写新的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(消息类型),随着项目的发展而不断出现。

roejwanj

roejwanj1#

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

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

希望这能帮上忙

eni9jsuy

eni9jsuy2#

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);
dgsult0t

dgsult0t3#

需要考虑的一部分是XML格式有多大的不同。特别是,它们都可以通过使用XML序列化的覆盖特性来实现吗?这允许您提供一个条目数组,覆盖诸如元素或属性名称/是否序列化为元素或属性等特性。
这可能允许您的各种格式只在传递给序列化过程的重写数组方面有所不同,这将给您留下如何确定要传递的数组的问题。

wd2eg0qa

wd2eg0qa4#

阅读有关自定义XML序列化程序的内容,它们应该能够完成您所需要的一切工作。
Link

相关问题