Динамически контролировать имя элемента XML во время сериализации - PullRequest
2 голосов
/ 30 июня 2011

вот моя проблема, которую нужно решить: у меня есть плагин-структура для мультимедийных шоу, которая позволяет реализовывать типы мультимедиа во внешних сборках путем создания подклассов из базового класса в моей среде.Шоу содержит список типов медиа.Шоу загружаются и сохраняются в формате XML с использованием XmlSerializer.Это все работает, даже с программным отображением типов для плагина MediaTypes.

Однако я хочу иметь возможность загружать XML-файлы, содержащие MediaTypes , которые не известны , поскольку плагин недоступен.

Для иллюстрации вот такой файл XML:

<MultiMediaShow>
    <MediaTypes>
        <SomeType />
        <SomeType />
        <AnotherType />
        <UnknownTypeFromPluginNotLoaded />
    </MediaTypes> 
</MultiMediaShow>

В приведенном выше примере я предполагаю 2 "известных" MediaTypes SomeType и AnotherType, исходящих из 2 сборок плагинов,Третий тип (UnknownTypeFromPluginNotLoaded) неизвестен.

Я уже могу десериализовать такие неизвестные объекты, но борюсь с сериализацией.В моем приложении у меня есть следующий код:

// To be implemented / subclassed by plugin assembly types
public abstract class MediaType
{
}

public class UnknownMediaType : MediaType 
{
    [XmlAnyElement]
    public XmlElement[] UnknownChildElements;
    [XmlAnyAttribute]
    public XmlAttibute[] UnknownAttributes;
    [XmlIgnore]
    public string XmlTagName;   // stores the xml element tag name in the original document
}

[XmlRoot("MultimediaShow")]
public class MultimediaShow
{
    public List<MediaType> MediaTypes = new List<MediaType>();
}

При десериализации этого с помощью XmlSerializer я использую событие UnknownElement и вручную вставляю элемент UnknownMediaType в show.MediaTypes:

void HandleUnknownElements(Show show, List<XmlElementEventArgs> unknownElementEvents, XmlAttributeOverrides overrides)
{
    // add a root attribute to UnknownMediaType
    XmlAttributes attrs = new XmlAttributes();
    XmlRootAttribute xmlRoot = new XmlRootAttribute(e.Element.Name);
    attrs.XmlRoot = xmlRoot;
    XmlAttributeOverrides o = new XmlAttributeOverrides();
    o.Add(typeof(UnknownMediaObject), attrs);

    // use a new XmlSerializer and a memory stream for deserializting the object as UnknownMediaType.
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(UnknownMediaType), o);
    using (MemoryStream memoryStream = new MemoryStream())
    {
        XmlDocument doc = new XmlDocument();
        doc.AppendChild(doc.ImportNode(e.Element, true));
        doc.Save(memoryStream);
        memoryStream.Position = 0;

        try
        {
            // deserialize the object, store the XML element name and insert it into the chapter
            UnknownMediaType t = xmlSerializer.Deserialize(memoryStream) as UnknownMediaObject;
            t.XmlTagName = e.Element.Name;
            show.MediaTypes.Add(t);
        }
        catch (Exception exc)
        {
            System.Diagnostics.Debug.WriteLine(exc.ToString());
            //return objectType.IsByRef ? null : Activator.CreateInstance(objectType);
        }
    }
}

Проблема BIG BIG заключается в том, что такое событие недоступно при сериализации объекта.В результате я получаю (не очень удивительно):

<MultiMediaShow>
    <MediaTypes>
        <SomeType />
        <SomeType />
        <AnotherType /> 
        <UnknownMediaType />    // !!!! was 'UnknownTypeFromPluginNotLoaded' !!!!
    </MediaTypes> 
</MultiMediaShow>

Однако, это явно не то, что я десериализовал.Итак, вопрос в том, как бы я лучше всего решил эту проблему?!?!

Вся помощь высоко ценится !!

Ура, Феликс

ОБНОВЛЕНИЕ

Мне было интересно, можно ли программно генерировать классы, производные от UnknownMediaType и получившие имя класса из UnknownMediaType.XmlTagName.Или, альтернативно, иметь атрибут для указания имени тега XML класса ??

Cheers, Felix

Ответы [ 3 ]

2 голосов
/ 04 июля 2011

На самом деле, я реализовал некоторое рабочее решение, основанное на типах зданий, динамически.Пока что я делаю то, что хочу.Единственный недостаток, который я вижу на данный момент, заключается в том, что я создаю их в текущем домене приложения, поэтому я не могу выгрузить их (например, если загружено новое шоу или если плагины будут доступны во время выполнения).

Вот мой код:

    void HandleUnknownElements(Show show, List<XmlElementEventArgs> unknownElementEvents, XmlAttributeOverrides overrides)
    {
        foreach (XmlElementEventArgs e in unknownElementEvents)
        {
            // (1) Dynamically create a type that simply inherits from UnknownMediaType 

            AssemblyName assName = new AssemblyName("Show Dynamic Type " + e.Element.Name + " Assembly");
            AssemblyBuilder assBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
            ModuleBuilder modBuilder = assBuilder.DefineDynamicModule(assBuilder.GetName().Name);

            TypeBuilder typeBuilder = modBuilder.DefineType(e.Element.Name, TypeAttributes.Class | TypeAttributes.Public, typeof(UnknownMediaType));
            Type objectType = typeBuilder.CreateType();


            // (2) Add a root attribute to the type as override

            XmlAttributes attrs = new XmlAttributes();
            XmlRootAttribute xmlRoot = new XmlRootAttribute(e.Element.Name);
            attrs.XmlRoot = xmlRoot;
            XmlAttributeOverrides o = new XmlAttributeOverrides();
            o.Add(objectType, attrs);


            // (3) Use a memory stream for creating a temporary XML document that will be deserialized

            using (MemoryStream memoryStream = new MemoryStream())
            {
                XmlDocument doc = new XmlDocument();
                doc.AppendChild(doc.ImportNode(e.Element, true));
                doc.Save(memoryStream);
                memoryStream.Position = 0;


                // (4) Deserialize the object using an XmlSerializer and add it

                try
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(objectType, o);
                    UnknownMediaType t = xmlSerializer.Deserialize(memoryStream) as UnknownMediaType;
                    show.MediaTypes.Add(t);
                }
                catch (Exception exc)
                {
                    System.Diagnostics.Debug.WriteLine(exc.ToString());
                }
            }
        }
    }

Буду рад, если вы опубликуете какие-либо проблемы с этим и вашими проблемами ...

Ура, Феликс

1 голос
/ 30 июня 2011

Узнайте, можете ли вы реализовать интерфейс IXmlSerializable для вашего корневого класса.

Из MSDN:

Есть две причины для реализации этот интерфейс. Во-первых, это контролировать, как ваш объект сериализуется или десериализован XmlSerializer. Например, вы можете портировать данные в байты вместо буферизации больших данных устанавливает, а также избежать инфляции что происходит, когда данные закодированы используя кодировку Base64. Контролировать сериализация, реализовать ReadXml и методы WriteXml для управления Используемые классы XmlReader и XmlWriter читать и писать XML. Для Пример этого см. в разделе «Как: Чанк». Сериализованные данные.

Вторая причина заключается в возможности контролировать схему. Чтобы включить это, Вы должны применить XmlSchemaProviderAttribute to сериализуемый тип и укажите имя статического члена, который возвращает схема. Увидеть XmlSchemaProviderAttribute для Пример.

Класс, который реализует интерфейс должен иметь конструктор без параметров. Это требование Класс XmlSerializer

0 голосов
/ 30 июня 2011

Вы можете реализовать клиентский сериализатор.Это больше работы, чем использование xmlserializer, но дает вам полный контроль.Смотри: http://msdn.microsoft.com/en-us/library/ty01x675(v=vs.80).aspx

...