Сериализация объектов передачи данных в .NET - PullRequest
6 голосов
/ 23 января 2010

У меня есть набор объектов передачи данных (например, много запросов, классов ответных сообщений, таких как MainRequest, MainResponse, ShutDownRequest, ShutDownResponse) Где новые классы продолжают поступать по мере развития проекта . Эти классы должны быть (де) сериализованы из и в различных форматов XML с различными общедоступными XSD . Новые форматы XML появляются по мере развития проекта.

Мой вопрос здесь заключается в том, как мне спроектировать свои классы и интерфейсы вокруг этих двух требований , особенно там, где я должен поместить действительную (де) сериализацию. Должен ли я написать статический сервис, который может принимать различные экземпляры 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.

myMainRequestInstace.Serialize(FormatTypeEnum type, XmlWriter writer);

Или есть совершенно другой подход? Должен ли я ввести общий интерфейс для сериализации и иметь некоторую инверсию управления, чтобы я мог загружать сериализаторы нового формата во время выполнения?

Какой шаблон дизайна может мне здесь помочь?

Какой открытый исходный код в мире .NET я мог бы изучить, чтобы увидеть различные подходы к этой теме?

EDIT: Я знаю об общих методах сериализации, существующих в рамках. Мой вопрос больше ориентирован на дизайн классов, который учитывает два требования: несколько форматов xml и несколько DTO (типов сообщений), которые продолжают поступать по мере развития проекта.

Ответы [ 4 ]

5 голосов
/ 23 января 2010

Наилучшим подходом было бы что-то вроде этого, это мой любимый подход:

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, старые привычки сильно умирают.

Обратите внимание, как в приведенном выше примере кода я использовал два разных метода HandleVersionOnePtZero и HandleVersionOnePtOne для обработки разных версий сериализованного потока. Делая это таким образом, я получаю большую степень гибкости, скажем, что если поле 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

Надеюсь, это поможет

3 голосов
/ 23 января 2010

В .NET уже есть некоторые встроенные механизмы для выполнения сериализации xml.

Сериализация на основе атрибутов - обзор . Все, что вам нужно сделать, это пометить членов вашего класса атрибутами и использовать класс 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);
1 голос
/ 23 января 2010

Частью рассмотрения будет то, насколько различны форматы XML. В частности, могут ли они быть достигнуты с помощью функции переопределения в XML-сериализации? Это позволяет вам предоставлять массив записей, переопределяющих такие функции, как имя элемента или атрибута / сериализация в виде элементов или атрибутов и т. Д.

Это может позволить вам различать различные форматы только с точки зрения переопределенного массива, передаваемого процессу сериализации. Это оставит вас с вопросом о том, как определить массив для передачи.

0 голосов
/ 23 января 2010

Читайте о пользовательских XML-сериализаторах, они должны иметь возможность делать все, что вам нужно.

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

...