【问题标题】:Generating an Xml Serialization assembly for a custom XmlSerializer为自定义 XmlSerializer 生成 Xml 序列化程序集
【发布时间】:2011-11-12 02:50:22
【问题描述】:

我的类中有一些方法可以使用与使用默认序列化程序生成的 XML 结构不同的 XML 结构进行序列化/反序列化。这些方法为我的类型创建了一个 XmlSerializer,但有一大堆覆盖。当这些方法被调用时,我猜内部.NET 仍然会生成一个序列化程序集,但我想在编译后生成这个程序集,所以它不会在运行时生成。如何为此自定义序列化生成序列化程序集?如果我对该类型使用 sgen.exe,它似乎只生成默认序列化程序。

特别是我需要生成序列化程序集的原因是我的代码是从 Internet Explorer 进程中调用的,该进程在保护模式下运行。如果 .net 运行时尝试生成序列化程序集,它会调用 csc.exe 并提示用户,询问是否允许运行此进程。我不希望用户被提示!所以需要在没有 csc.exe 的情况下完成所有的序列化/反序列化。

我能想到的一个选项是捕获在运行时生成的 .cs,将其放入单独的程序集中,然后将其包含在我的产品中。除了有点恶心之外,这还引发了如何在我的构建过程中自动生成该 .cs 的问题......

也许还值得一提:我的自定义 xml 序列化也序列化嵌套类,所以也许也会有自动生成的序列化器。我的自定义序列化主要是按照以下几行完成的——它将 XmlIgnore 添加到某些属性并从其他属性中删除 XmlIgnore。其中许多属性返回需要序列化的对象,其中一些实现 IXmlSerializable,一些使用默认序列化。

    public void SerializeToCustomXml1(XmlWriter writer)
    {
        try
        {
            CustomXmlSerializer1.Serialize(writer, this);
        }
        catch (Exception)
        {
        }
    }

    /// <summary>
    /// Static serializer so it's created just once. There's another one like this CustomXmlSerializer2 with a slightly different format again.
    /// </summary>
    private static XmlSerializer CustomXmlSerializer1
    {
        get
        {
            if (_customXmlSerializer == null)
            {
                XmlAttributes dontIgnore = new XmlAttributes();
                dontIgnore.XmlIgnore = false;

                XmlAttributes attributes;
                XmlAttributeOverrides overrides = new XmlAttributeOverrides();

                // Include some fields in the XML that wouldn't be there otherwise.
                overrides.Add(typeof (WebResource), "ID", dontIgnore);
                overrides.Add(typeof (WebResource), "HasDestinationURLs", dontIgnore);
                overrides.Add(typeof (Resource), "AccessDefindBy", dontIgnore);

                attributes = new XmlAttributes();
                attributes.XmlIgnore = false;
                attributes.XmlElements.Add(new XmlElementAttribute("ActionID"));
                overrides.Add(typeof(Action), "ID", attributes);

                // Instead of serializing the Actions field we serialize CustomActionsXmlSerializer,
                // which outputs different content in the XML
                overrides.Add(typeof (WebResource), "Actions", ignore);
                attributes = new XmlAttributes();
                attributes.XmlIgnore = false;
                attributes.XmlElements.Add(new XmlElementAttribute("Actions"));
                overrides.Add(typeof (WebResource), "CustomActionsXmlSerializer", attributes);                  

                // ... more of these overrides here ... 
                _customXmlSerializer1 = new XmlSerializer(typeof(WebResource), overrides);
            }
            return _customXmlSerializer1;
        }

交叉发布herehere

更新: 哦,this answer 表明这是不可能的,因为如果您使用 XmlOverrides,XmlSerializer 甚至不会寻找预编译的程序集。该死。然后我想我最好的选择是生成序列化代码并将其包含在我的项目中并直接调用它而不是调用 XmlSerializer。关于如何巧妙地做到这一点有什么想法吗?

【问题讨论】:

  • 啊,我直到现在才看到您正在使用 XmlOverrides。你真的需要这样做吗,或者你可以通过实现IXmlSerializable 来达到同样的效果吗?从您的代码中,我想说您对覆盖所做的大部分事情都应该通过使用静态[XmlAttribute]s 和IXmlSerializable 来实现。
  • 我使用 XmlOverrides 是因为我要序列化到三种不同的 XML 格式。一个使用默认序列化,另外两个使用带有一大堆覆盖的 XmlSerializer 对象。我认为 IXmlSerializable 只会让我选择输出为一种格式。
  • 使用IXmlSerializable,您可以从XmlReaderXmlWriter 手动读取和写入元素和属性,因此您可以完全控制要如何读取和写入数据.考虑到您要序列化或反序列化的上下文,您可以执行任意数量的ifs 并拥有任意数量的代码路径。
  • 您将如何使用它来生成不同格式的 Xml,即 Read/WriteXml 方法如何确定上下文?我想我可以在类中放置一个 DesiredFormat 字段,然后 serialize 方法将设置该 DesiredFormat 然后调用 Serialize/Deserialize,ReadXml() 和 WriteXml() 方法将首先检查 DesiredFormat 以查看它们应该使用哪种格式输出?这样做的一个缺点是我有嵌套类,每个类都需要它们的 DesiredFormat 集。手动编码 ReadXml 和 WriteXml 也比使用属性更繁琐且容易出错..:(
  • 实现IXmlSerializable 确实很麻烦。如果不完全了解不同的上下文以及为什么需要切换,很难说应该如何进行上下文切换。随时详细说明您的问题;每种格式的示例代码都会很棒。

标签: c# .net xml-serialization sgen


【解决方案1】:

如果您通过“自定义 XmlSerializer”表示您使用带有自定义序列化代码的 System.Xml.XmlSerializer(通过 IXmlSerializable[XmlElement] 属性和朋友),它应该在序列化时寻找名为 MyAssembly.XmlSerializers.dll 的程序集。

程序集的命名很重要,如果您不确定是否正在查找它以及正在查找的确切程序集名称,您可以将一个事件处理程序附加到AppDomain.CurrentDomain.FirstChanceException,它将触发所有异常应用程序域,包括程序集加载异常。您还可以挂钩 AppDomain.CurrentDomain.AssemblyLoadAppDomain.CurrentDomain.AssemblyResolve 事件,每次首次将程序集加载到应用程序域时都会触发这些事件。

另一个重要的事情是MyAssembly.dllMyAssembly.XmlSerializers.dll 的版本(也许是关键;我不确定)必须匹配,并且它们也应该并排放置。请参阅thisthis MSDN article 了解更多信息。

【讨论】:

  • 是的,我创建了一个带有各种覆盖的 XmlSerializer。将发布一些代码。
  • 谢谢,我会看看 FirstChanceException 看看会发生什么。我认为它必须寻找与 MyAssembly.XmlSerializers.dll 不同的程序集,或其中的不同类,因为它需要为每个 new XmlSerializer(theType, overrides) 每个 theType 提供不同的类并覆盖参数。鉴于文档说当您使用覆盖创建序列化程序时它不会缓存序列化程序,我想知道它是否会费心寻找现有程序集......但是寻找程序集加载异常或通过 procmon 应该会有所帮助。
  • 你也可以试试我提到的AssemblyLoadAssemblyResolve事件。
【解决方案2】:

(还没有足够的积分来评论)

您应该考虑硬着头皮实施 IXmlSerializable,让您可以决定什么要序列化,什么不序列化,而无需在运行时更改属性。定义您自己的属性、构造函数要求并将它们序列化。

也许您甚至可以放弃 XML-Serialization 并切换到 JSon.Net,这样不太可能需要跳过这些障碍。

【讨论】:

    猜你喜欢
    • 2010-12-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    • 2010-09-13
    相关资源
    最近更新 更多