在序列化过程中动态控制 XML 元素名称

发布于 2024-11-18 00:42:21 字数 3440 浏览 2 评论 0原文

这是我要解决的问题:我有一个多媒体节目的插件结构,它允许通过从我的框架中的基类进行子类化来实现外部程序集中的媒体类型。节目包含媒体类型列表。使用 XmlSerializer 以 XML 形式加载和保存节目。即使使用插件 MediaTypes 的编程类型映射,这一切都有效。

但是,我希望能够加载包含未知媒体类型的 XML 文件,因为该插件不可用。

为了便于说明,这里有一个这样的 XML 文件:

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

在上面的示例中,我假设 2 个“已知”MediaTypes SomeTypeAnotherType,来自 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>

但是,这显然与我反序列化的内容不同。所以问题是,我该如何最好地解决这个问题?!?!

高度赞赏所有帮助!

干杯, Felix


更新

我想知道是否可以以编程方式生成从 UnknownMediaType 派生的类,并且类名取自 UnknownMediaType.XmlTagName。或者,也可以有一个属性来指定类的 XML 标记名称?

干杯, 菲利克斯

here's my problem to be solved: I've a plugin-structure for multimedia shows that allows to implement media types in external assemblies by subclassing from a base class in my framework. A show holds a list of media types. Shows are loaded and saved in XML using the XmlSerializer. This all works, even with programatic type mapping for plugin MediaTypes.

However, I want to be able to load XML files that contain MediaTypes that are not known, because the plugin isn't available.

For illustration, here is such an XML file:

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

In the above example, I assume 2 "known" MediaTypes SomeType and AnotherType, comming from 2 plugin assemblies. The third type (UnknownTypeFromPluginNotLoaded) is unknown.

I'm already able to deserialize such unknown objects, but struggle with the serialization. In my aplication, I've the following code so far:

// 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>();
}

When deserializing this with XmlSerializer, I use the event UnknownElement and manually insert an UnknownMediaType element into 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);
        }
    }
}

The BIG BIG problem is that such an event doesn't seem to be available when serializing an object. What I get as output (not very surpising) is:

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

However, this is obviously not the same as what I've deserialized. So the question is, how would I best solve this problem?!?!

All help highly appreciated!!

Cheers,
Felix


UPDATE

I was wondering if it is possible to generate classes programmatically that derive from UnknownMediaType and have the class name taken from the UnknownMediaType.XmlTagName. Or, alternativly, to have an attribute for specifying the XML tag name of a class??

Cheers,
Felix

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

踏月而来 2024-11-25 00:42:21

事实上,我动态地实现了一些基于构建类型的工作解决方案。到目前为止,它正在做我想做的事情。目前我看到的唯一缺点是我将它们创建到当前应用程序域中,因此我无法卸载它们(例如,如果加载新节目或插件在运行时可用)。

这是我的代码:

    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());
                }
            }
        }
    }

如果您发布任何与此有关的问题和您的疑虑,我会很高兴...

干杯,
菲利克斯

In fact, I implemented some working solution based on building types dynamically. So far, it's doing what I want. The only downside I see at the moment is that I create them into the current app domain, so I can't unload them (e.g. if a new show is loaded or if the plugins would be made available at runtime).

Here's my code:

    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());
                }
            }
        }
    }

Would be glad if you'd post any issues with this and your concerns...

Cheers,
Felix

睫毛溺水了 2024-11-25 00:42:21

查看是否可以实现 IXmlSerialized 根类的接口。

来自 MSDN:

实施的原因有两个
这个界面。第一个是
控制对象的序列化方式
或由 XmlSerializer 反序列化。
例如,您可以将数据分块到
字节而不是缓冲大数据
套,还可以避免通货膨胀
当数据被编码时发生
使用 Base64 编码。为了控制
序列化,实现ReadXml
和 WriteXml 方法来控制
使用的 XmlReader 和 XmlWriter 类
读取和写入 XML。对于一个
有关示例,请参阅如何:分块
序列化数据。

第二个原因是能够
控制架构。为了实现这一点,
您必须应用
XmlSchemaProviderAttribute 到
可序列化类型,并指定
返回的静态成员的名称
架构。请参阅
XmlSchemaProviderAttribute 的
示例。

实现接口的类
必须有一个无参数构造函数。
这是一个要求
XmlSerializer类

See whether you can implement the IXmlSerializable interface for your root class.

From MSDN:

There are two reasons to implement
this interface. The first is to
control how your object is serialized
or deserialized by the XmlSerializer.
For example, you can chunk data into
bytes instead of buffering large data
sets, and also avoid the inflation
that occurs when the data is encoded
using Base64 encoding. To control the
serialization, implement the ReadXml
and WriteXml methods to control the
XmlReader and XmlWriter classes used
to read and write the XML. For an
example of this, see How To: Chunk
Serialized Data.

The second reason is to be able to
control the schema. To enable this,
you must apply the
XmlSchemaProviderAttribute to the
serializable type, and specify the
name of the static member that returns
the schema. See the
XmlSchemaProviderAttribute for an
example.

A class that implements the interface
must have a parameterless constructor.
This is a requirement of the
XmlSerializer class

对风讲故事 2024-11-25 00:42:21

您可以实现客户序列化器。它比使用 xmlserializer 需要更多工作,但可以让您完全控制。请参阅:http://msdn.microsoft.com/ en-us/library/ty01x675(v=vs.80).aspx

You can implement a customer serializer. It is more work than using the xmlserializer, but gives you full control. See: http://msdn.microsoft.com/en-us/library/ty01x675(v=vs.80).aspx

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文