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

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

Но независимо от того, какой код использует библиотеку, не обязательно знать о внутренних классах. Мои внутренние объекты сериализуются в 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();
    }
}

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

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

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

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

28
задан Mike Corcoran 10 January 2014 в 19:21
поделиться

2 ответа

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

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

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

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

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

Проницательные читатели: «Имя сборки известно, что кто-то использует ' sgen.exe ' "

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

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

Они могут быть правы! Если я попрошу проницательных читателей написать мне образец, они, вероятно, напишут что-то вроде этого. (Отказ от ответственности: это не официальное решение. 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
21
ответ дан 28 November 2019 в 03:55
поделиться

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

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

-2
ответ дан 28 November 2019 в 03:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: