2

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?

Community
  • 1
  • 1
Rory
  • 40,559
  • 52
  • 175
  • 261
  • Ah, I didn't see until now that you're using XmlOverrides. Do you really need to do that or can you achieve the same through implementing `IXmlSerializable`? From your code, I'd say most of the things you're doing with the overrides should be possible through the use of static `[XmlAttribute]`s and `IXmlSerializable`. – Asbjørn Ulsberg Sep 08 '11 at 08:13
  • I'm using XmlOverrides because I have three different XML formats that I'm serializing to. One uses the default serialization and the other two use XmlSerializer objects with a whole bunch of overrides. I think that IXmlSerializable would only give me the option to output to one format. – Rory Sep 08 '11 at 09:21
  • With `IXmlSerializable`, you read and write elements and attributes manually from an `XmlReader` or to an `XmlWriter`, so you're in full control of how you want to read and write your data. You can do as many `if`s and have as many code paths you want, given the context you're serializing or deserializing in. – Asbjørn Ulsberg Sep 08 '11 at 11:01
  • How would you use that to generate different formats of Xml, i.e. how do the Read/WriteXml methods determine the context? I suppose I could put a DesiredFormat field in the class, then the serialize method would set that DesiredFormat and then call Serialize/Deserialize, and the ReadXml() and WriteXml() methods would first check DesiredFormat to see which format they're supposed to be outputting? One downside with that is that I have nested classes which would each need their DesiredFormat set. Manually coding ReadXml and WriteXml is also more tedious and error-prone than using attributes..:( – Rory Sep 08 '11 at 14:05
  • Implementing `IXmlSerializable` is indeed a drag. It's difficult to say how you should do the context switching without fully understanding the different contexts and why you need to switch. Feel free to elaborate in your question; example code for each format would be great. – Asbjørn Ulsberg Sep 08 '11 at 22:06

2 Answers2

2

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.

Asbjørn Ulsberg
  • 8,721
  • 3
  • 45
  • 61
  • Yes, I create an XmlSerializer with various overrides. Will post some code. – Rory Sep 07 '11 at 23:35
  • Thanks, I will look at FirstChanceException and see what happens. I presume it must look for a different assembly than MyAssembly.XmlSerializers.dll, or a different class in it, since it'll need a different class for each `new XmlSerializer(theType, overrides)` per theType and overrides params. Given that the docs say it doesn't cache serializers when you create them with overrides I wonder if it bothers looking for an existing assembly at all ... but looking for the assembly load exception or via procmon should help. – Rory Sep 07 '11 at 23:47
  • You can try the `AssemblyLoad` and `AssemblyResolve` events I've mentioned too. – Asbjørn Ulsberg Sep 08 '11 at 08:11
1

(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.

aquaherd
  • 444
  • 4
  • 12