为自定义 XmlSerializer 生成 Xml 序列化程序集

发布于 2024-12-03 03:35:19 字数 3330 浏览 1 评论 0 原文

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

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

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

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

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

交叉发布此处此处

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

I have methods in my class for serializing/deserializing using a different XML structure than what would be produced with the default serializer. The methods create an XmlSerializer for my type but with a whole bunch of overrides. When these methods are called I guess internally .NET still generates a serialization assembly but I would like to generate this assembly after compilation so it's not generated at runtime. How can I generate a serialization assembly for this custom serialization? If I use sgen.exe for that type it appears to only generate the default serializer.

In particular the reason I need to generate the serialization assembly is that my code is called from within Internet Explorer process, which runs in Protected Mode. If the .net runtime tries to generate a serialization assembly it calls csc.exe and the user is prompted, asking whether to allow this process to be run. I don't want users to be prompted! So need all serialization/deserialization to be done without csc.exe.

One option I can think of is capturing that .cs that's generated at runtime, putting it in a separate assembly and then including that in my product. Apart from being a bit icky, that raises the problem of how to auto-generate that .cs as part of my build process ...

Perhaps also worth mentioning: my custom xml serialization also serializes nested classes, so maybe there would be auto-generated serializers for them too. My custom serialization is mostly done along the lines below - it adds XmlIgnore to some properties and removes XmlIgnore from others. Many of these properties return objects that would need to be serialized, some of which implement IXmlSerializable, some use default serialization.

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

Cross-posted here and here.

UPDATE:
Oh, this answer suggests it's just not possible as XmlSerializer doesn't even look for a precompiled assembly if you're using XmlOverrides. Darn. Then I guess my best option is to generate the serialization code and include it in my project and call that directly instead of calling XmlSerializer. Any thoughts on how to do this neatly?

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

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

发布评论

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

评论(2

凉墨 2024-12-10 03:35:19

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

程序集的命名很重要,如果您不确定是否正在查找它以及正在查找的确切程序集名称是什么,您可以将事件处理程序附加到 AppDomain.CurrentDomain.FirstChanceException 它将针对应用程序中的所有异常触发域,包括程序集加载异常。您还可以连接到 AppDomain.CurrentDomain.AssemblyLoad< /code>AppDomain.CurrentDomain.AssemblyResolve 事件,每次程序集首次加载到应用程序域时都应触发。

另一件重要的事情是 MyAssembly.dllMyAssembly.XmlSerializers.dll 的版本(也许是关键;我不确定)必须匹配,并且也应该放置彼此并肩。请参阅这篇 MSDN 文章 了解更多信息 信息。

If you by "custom XmlSerializer" mean that you use System.Xml.XmlSerializer with custom serialization code (either through IXmlSerializable or [XmlElement] attributes and friends), it should be looking for an assembly called MyAssembly.XmlSerializers.dll when serializing.

The naming of the assembly is important, and if you're unsure whether it's being looked for and what exact assembly name is being looked up, you can just attach an event handler to AppDomain.CurrentDomain.FirstChanceException which will fire for all exceptions in the app domain, including assembly loading exceptions. You can also hook into the AppDomain.CurrentDomain.AssemblyLoad and AppDomain.CurrentDomain.AssemblyResolve events, which should fire every time an assembly is first loaded into the app domain.

Another important thing is that the version (and perhaps key; I'm not sure) of MyAssembly.dll and MyAssembly.XmlSerializers.dll must match and they should also be placed alongside each other. See this and this MSDN article for more information.

万人眼中万个我 2024-12-10 03:35:19

(还没有足够的要点来评论)

您应该考虑咬紧牙关并以允许您决定序列化什么和不序列化什么的方式实现 IXmlSerialized,而无需在运行时更改属性。定义您自己的属性、构造函数要求并将它们序列化。

也许您甚至可以放弃 XML 序列化并切换到 Json.Net,因为不太可能需要跳过这样的麻烦。

(Not yet enough points to comment)

You should consider biting the bullet and implement IXmlSerializable in a manner that allows you to decide what to serialize and what not, without changing attributes at runtime. Define your own attributes, constructor requirements and serialize them.

Perhaps you could even sack XML-Serialization and switch to JSon.Net where the need to jump through such hoops is rather unlikely.

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