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

时间:2011-06-30 12:27:16

标签: c# serialization xml-serialization xmlserializer

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

但是,我希望能够加载包含未知的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>

但是,这显然与我反序列化的不一样。所以问题是,我怎样才能最好地解决这个问题?!?!

所有人都非常感谢!!

干杯, 菲利克斯


更新

我想知道是否有可能以编程方式生成派生自UnknownMediaType的类并且具有取自UnknownMediaType.XmlTagName的类名。或者,替代地,具有用于指定类的XML标记名称的属性??

干杯, 菲利克斯

3 个答案:

答案 0 :(得分:2)

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

这是我的代码:

    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 :(得分:0)

了解您是否可以为根类实施 IXmlSerializable 接口。

来自MSDN:

  

实施有两个原因   这个界面。首先是   控制对象的序列化方式   或由XmlSerializer反序列化。   例如,您可以将数据块化为   字节而不是缓冲大数据   集合,也避免了通货膨胀   数据编码时发生的情况   使用Base64编码。要控制   序列化,实现ReadXml   和WriteXml方法来控制   使用了XmlReader和XmlWriter类   读写XML。为   例如,请参见如何:块   序列化数据。

     

第二个原因是能够   控制架构。为此,   你必须申请   XmlSchemaProviderAttribute到   serializable类型,并指定   返回的静态成员的名称   架构。见   XmlSchemaProviderAttribute用于   示例

     

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

答案 2 :(得分:0)

您可以实施客户序列化程序。这比使用xmlserializer更有效,但可以完全控制。请参阅:http://msdn.microsoft.com/en-us/library/ty01x675(v=vs.80).aspx