在序列化期间动态控制XML元素名称

本文关键字:元素 XML 动态控制 序列化 | 更新日期: 2023-09-27 18:00:51

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

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

举例来说,这里有这样一个XML文件:

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

在上面的例子中,我假设2个"已知"的媒体类型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标记名的属性??

干杯,Felix

在序列化期间动态控制XML元素名称

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

这是我的代码:

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

如果你能发布任何与此有关的问题和你的担忧,我会很高兴。。。

干杯,Felix

查看是否可以为根类实现IXmlSerializable接口。

来自MSDN:

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

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

实现接口的类必须具有无参数构造函数。这是XmlSerializer类

您可以实现一个客户序列化程序。它比使用xmlserializer工作量更大,但提供了完全的控制权。请参阅:http://msdn.microsoft.com/en-us/library/ty01x675(v=vs.80(.aspx

相关文章: