Как я могу сериализовать внутренние classы с помощью XmlSerializer?

Я создаю библиотеку для взаимодействия с третьей стороной. Связь осуществляется через сообщения XML и HTTP. Это работает.

Но, независимо от того, какой код использует библиотеку, не нужно знать о внутренних classах. С помощью этого метода мои внутренние объекты сериализуются в XML:

internal static string SerializeXML(Object obj) { XmlSerializer serializer = new XmlSerializer(obj.GetType(), "some.domain"); //settings XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; using (StringWriter stream = new StringWriter()) { using (XmlWriter writer = XmlWriter.Create(stream, settings)) { serializer.Serialize(writer, obj); } return stream.ToString(); } } 

Однако, когда я изменяю модификатор доступа моих classов к internal , я получаю исключение во время выполнения :

[System.InvalidOperationException] = {«MyNamespace.MyClass недоступен из-за уровня защиты. Можно обрабатывать только общедоступные типы».}

Это исключение происходит в первой строке вышеприведенного кода.

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

Из блога Sowmy Srinivasan – Сериализация внутренних типов с помощью XmlSerializer :

Возможность сериализации внутренних типов является одним из распространенных запросов, которые видит команда XmlSerializer . Это разумный запрос от людей, отправляющих библиотеки. Они не хотят, чтобы типы XmlSerializer были открыты только для сериализатора. Недавно я перешел из команды, которая написала XmlSerializer для команды, которая потребляет XmlSerializer. Когда я наткнулся на подобную просьбу, я сказал: «Нет, используйте DataContractSerializer ».

Причина проста. XmlSerializer работает, создавая код. Сгенерированный код живет в динамически сгенерированной сборке и должен обращаться к типам, которые сериализуются. Поскольку XmlSerializer был разработан за некоторое время до появления легкого генерации кода , сгенерированный код не может получить доступ к чему-либо другому, кроме общедоступных, в другой сборке. Следовательно, типы, которые сериализуются, должны быть общедоступными.

Я слышал, как проницательные читатели шепчут: «Это не обязательно быть публичным, если используется атрибут InternalsVisibleTo ».

Я говорю: «Правильно, но имя сгенерированной сборки неизвестно заранее. На какую сборку вы делаете внутренности видимыми?»

Астуальные читатели: «имя сборки известно, если вы используете« sgen.exe »»

Me: «Чтобы sgen генерировал сериализатор для ваших типов, они должны быть общедоступными»

Настоящие читатели: «Мы могли бы сделать компиляцию с двумя проходами. Один проход для sgen с типами, как открытый, и другой проход для доставки с типами как внутренними».

Они могут быть правы! Если я попрошу проницательных читателей написать мне образец, они, вероятно, напишут что-то вроде этого. (Отказ от ответственности: это не официальное решение. YMMV)

 using System; using System.IO; using System.Xml.Serialization; using System.Runtime.CompilerServices; using System.Reflection; [assembly: InternalsVisibleTo("Program.XmlSerializers")] namespace InternalTypesInXmlSerializer { class Program { static void Main(string[] args) { Address address = new Address(); address.Street = "One Microsoft Way"; address.City = "Redmond"; address.Zip = 98053; Order order = new Order(); order.BillTo = address; order.ShipTo = address; XmlSerializer xmlSerializer = GetSerializer(typeof(Order)); xmlSerializer.Serialize(Console.Out, order); } static XmlSerializer GetSerializer(Type type) { #if Pass1 return new XmlSerializer(type); #else Assembly serializersDll = Assembly.Load("Program.XmlSerializers"); Type xmlSerializerFactoryType = serializersDll.GetType("Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract"); MethodInfo getSerializerMethod = xmlSerializerFactoryType.GetMethod("GetSerializer", BindingFlags.Public | BindingFlags.Instance); return (XmlSerializer)getSerializerMethod.Invoke(Activator.CreateInstance(xmlSerializerFactoryType), new object[] { type }); #endif } } #if Pass1 public class Address #else internal class Address #endif { public string Street; public string City; public int Zip; } #if Pass1 public class Order #else internal class Order #endif { public Address ShipTo; public Address BillTo; } } 

Некоторые проницательные «взломанные» читатели могут зайти так далеко, как дать мне build.cmd, чтобы скомпилировать его.

 csc /d:Pass1 program.cs sgen program.exe csc program.cs 

Одной рабочей задачей является использование DataContractSerializer вместо XmlSerializer.

Также см. Это: http://blogs.msdn.com/b/sowmy/archive/2008/10/04/serializing-internal-types-using-xmlserializer.aspx

В качестве альтернативы вы можете использовать динамически созданные публичные classы (которые не будут доступны для сторонних разработчиков):

 static void Main() { var emailType = CreateEmailType(); dynamic email = Activator.CreateInstance(emailType); email.From = "[email protected]"; email.To = "[email protected]"; email.Subject = "Dynamic Type"; email.Boby = "XmlSerializer can use this!"; } static Type CreateEmailType() { var assemblyName = new AssemblyName("DynamicAssembly"); var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); var typeBuilder = moduleBuilder.DefineType( "Email", ( TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.SequentialLayout | TypeAttributes.Serializable ), typeof(ValueType) ); typeBuilder.DefineField("From", typeof(string), FieldAttributes.Public); typeBuilder.DefineField("To", typeof(string), FieldAttributes.Public); typeBuilder.DefineField("Subject", typeof(string), FieldAttributes.Public); typeBuilder.DefineField("Body", typeof(string), FieldAttributes.Public); return typeBuilder.CreateType(); } 

Это может помочь вам: MRB_ObjectSaver

Этот проект помогает вам сохранять / загружать / клонировать любой объект в c # в / из файла / строки. По сравнению с «сериализация c #» этот метод сохраняет ссылку на объекты, а связь между объектами не прерывается. (см. пример SerializeObjectTest.cs). Кроме того, этот тип не отмечен как [Serializable]

Вы также можете использовать что-то, называемое xgenplus (http://xgenplus.codeplex.com/), которое генерирует код, который обычно запускается во время выполнения. Затем вы добавляете это в свое решение и компилируете его как часть своего решения. В этот момент не имеет значения, является ли ваш объект внутренним – вы можете добавить предварительно сгенерированный код в том же пространстве имен. Производительность для этого быстро пылает, так как все ее создаются заранее.

  • Сериализация прерываний в .NET 4.5
  • Можете ли вы указать формат для XmlSerialization datetime?
  • Каков наилучший способ преобразования объекта java в xml с открытым исходным кодом apis
  • Сериализация объекта в строку
  • Весенние сеансовые компоненты (controllerы) и ссылки на службы с точки зрения сериализации
  • Сериализация XML и унаследованные типы
  • jackson: как добавить настраиваемое свойство в JSON без изменения POJO
  • Как сопоставить объекты XML с C #
  • $ (this) .serialize () - Как добавить значение?
  • Gson сериализует список полиморфных объектов
  • Любой способ заставить DataContractJsonSerializer правильно сериализовать словари?
  • Давайте будем гением компьютера.