Сериализация объектов передачи данных в.NET
У меня есть набор объектов передачи данных (например, много запросов, классов ответных сообщений, таких как 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 можно изучить, чтобы увидеть разные подходы к этой теме?
РЕДАКТИРОВАТЬ: я знаю об общих методах сериализации, существующих в рамках. Мой вопрос больше связан с дизайном классов, который учитывает два требования: несколько форматов xml и несколько DTO (типов сообщений), которые продолжают поступать по мере развития проекта.
4 ответа
Лучший подход будет примерно таким, это мой любимый подход:
открытый класс SomeClass: ISerializable{ закрытое плавание _fVersion; .... публичная версия get {return this._fVersion; } } private SomeClass (информация SerializationInfo, контекст StreamingContext) { bool bOk = false; this._fVersion = info.GetSingle("versionID"); if (this._fVersion == 1.0F) bOk = this.HandleVersionOnePtZero(информация, контекст); if (this._fVersion == 1.1F) bOk = this.HandleVersionOnePtOne(информация, контекст); if (! bOk) генерирует новое исключение SerializationException(string.Format("SomeClass: не удалось обработать эту версию {0}.", this._fVersion.ToString())); } public void GetObjectData(информация SerializationInfo, контекст StreamingContext) { 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, контекст StreamingContext) { bool rvOk = false;... = info.GetValue ("someField");... = info.GetValue ("anotherField"); } private bool HandleVersionOnePtOne (информация SerializationInfo, контекст StreamingContext) { bool rvOk = false;... = info.GetValue ("someField1");... = info.GetValue ("anotherField2"); } }
Вот как я применяю более жесткий контроль над сериализацией двоичных данных и увеличиваю версию. Теперь те из вас укажут, что уже есть возможность сделать это, но, начиная с.NET 1.1, старые привычки сильно умирают.
Обратите внимание, как в примере кода выше я использовал два разных метода HandleVersionOnePtZero
а также HandleVersionOnePtOne
обрабатывать разные версии сериализованного потока. Делая это таким образом, я имею большую степень гибкости, скажем, что если поле someField
нужно изменить? Кроме того, обратите внимание, как _fVersion
Поле - это первое, что сначала делает сериализуемая подпрограмма, затем проверяет версию поля и решает, какую из них использовать.
Единственное, что нужно сделать, это то, что если вы измените пространство имен, у вас возникнут трудности с десериализацией данных, но вы можете использовать класс SerializationBinder в качестве примера:
открытый класс SomeClassBinder: System.Runtime.Serialization.SerializationBinder {общедоступное переопределение Тип BindToType(string assemblyName, string typeName) { Type typeToDeserialize = null; try { // Для каждого AssemblyName/typeName, которые вы хотите десериализовать в // другой тип, установите typeToDeserialize на нужный тип. string assemblymVer1 = System.Reflection.Assembly.GetExecutingAssembly().FullName; if (assemblyName.StartsWith("foobar")) { assemblyName = assemblymVer1; 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; } } это будет называться так: BinaryFormatter bf = new BinaryFormatter(); bf.Binder = new SomeClassBinder(); SomeClass sc = bf.Deserialize(stream); // предположим, что поток объявлен и открыт
Надеюсь это поможет
Уже есть некоторые встроенные механизмы для выполнения сериализации XML в.NET.
Сериализация на основе атрибутов - обзор. Все, что вам нужно сделать, это пометить членов вашего класса атрибутами и использовать класс 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" });
Выход -
бар MyElement> MyXmlClass>
Или вы можете сделать так, чтобы все ваши сериализуемые классы xml реализовали IXmlSerializable.
Изменить - теперь, так как я неправильно понял ваш исходный вопрос, я исправлю это с помощью техники, которую вы могли бы использовать для сериализации одного и того же объекта в несколько разных форматов XML.
Теперь, когда ваш объект передачи данных можно сериализовать как минимум в один формат на основе xml, вы можете выполнить шаг пост-перевода, чтобы перевести его в нужный вам формат, используя преобразования xslt. 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);
Частью рассмотрения будет то, насколько разные форматы XML. В частности, все ли они могут быть выполнены с помощью функции переопределения в XML-сериализации? Это позволяет вам предоставлять массив записей, переопределяющих такие функции, как имя элемента или атрибута / сериализация в виде элементов или атрибутов и т. Д.
Это может позволить вам иметь различные форматы, отличающиеся только с точки зрения массива переопределения, передаваемого процессу сериализации. Это оставило бы вас с вопросом о том, как определить массив для передачи.
Читайте о пользовательских XML-сериализаторах, они должны быть в состоянии сделать все, что вам нужно.
http://geekswithblogs.net/marcel/archive/2006/05/19/78989.aspx