views:

225

answers:

1

Hi

I'm trying to use the System.Xml.Serialization.XmlSerializer to serialize a dynamically loaded (and compiled class). If I build the class in question into the main assembly, everything works as expected. But if I compile and load the class from an dynamically loaded assembly, the XmlSerializer throws an exception.

What am I doing wrong?

I've created the following .NET 3.5 C# application to reproduce the issue:

using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Text;
using System.Reflection;
using System.CodeDom.Compiler;
using Microsoft.CSharp;

public class StaticallyBuiltClass
{
    public class Item
    {
        public string Name { get; set; }
        public int Value { get; set; }
    }
    private List<Item> values = new List<Item>();
    public List<Item> Values { get { return values; } set { values = value; } }
}

static class Program
{
    static void Main()
    {
        RunStaticTest();
        RunDynamicTest();
    }

    static void RunStaticTest()
    {
        Console.WriteLine("-------------------------------------");
        Console.WriteLine(" Serializing StaticallyBuiltClass...");
        Console.WriteLine("-------------------------------------");
        var stat = new StaticallyBuiltClass();

        Serialize(stat.GetType(), stat);

        Console.WriteLine();
    }

    static void RunDynamicTest()
    {
        Console.WriteLine("-------------------------------------");
        Console.WriteLine(" Serializing DynamicallyBuiltClass...");
        Console.WriteLine("-------------------------------------");
        CSharpCodeProvider csProvider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v3.5" } });

        CompilerParameters csParams = new System.CodeDom.Compiler.CompilerParameters();
        csParams.GenerateInMemory = true;
        csParams.GenerateExecutable = false;
        csParams.ReferencedAssemblies.Add("System.dll");
        csParams.CompilerOptions = "/target:library";

        StringBuilder classDef = new StringBuilder();
        classDef.AppendLine("using System;");
        classDef.AppendLine("using System.Collections.Generic;");
        classDef.AppendLine("");
        classDef.AppendLine("public class DynamicallyBuiltClass");
        classDef.AppendLine("{");
        classDef.AppendLine("    public class Item");
        classDef.AppendLine("    {");
        classDef.AppendLine("        public string Name { get; set; }");
        classDef.AppendLine("        public int Value { get; set; }");
        classDef.AppendLine("    }");
        classDef.AppendLine("    private List<Item> values = new List<Item>();");
        classDef.AppendLine("    public List<Item> Values { get { return values; } set { values = value; } }");
        classDef.AppendLine("}");

        CompilerResults res = csProvider.CompileAssemblyFromSource(csParams, new string[] { classDef.ToString() });

        foreach (var line in res.Output)
        {
            Console.WriteLine(line);
        }

        Assembly asm = res.CompiledAssembly;
        if (asm != null)
        {
            Type t = asm.GetType("DynamicallyBuiltClass");
            object o = t.InvokeMember("", BindingFlags.CreateInstance, null, null, null);
            Serialize(t, o);
        }

        Console.WriteLine();
    }

    static void Serialize(Type type, object o)
    {
        var serializer = new XmlSerializer(type);
        try
        {
            serializer.Serialize(Console.Out, o);
        }
        catch(Exception ex)
        {
            Console.WriteLine("Exception caught while serializing " + type.ToString());
            Exception e = ex;
            while (e != null)
            {
                Console.WriteLine(e.Message);
                e = e.InnerException;
                Console.Write("Inner: ");
            }
            Console.WriteLine("null");
            Console.WriteLine();
            Console.WriteLine("Stack trace:");
            Console.WriteLine(ex.StackTrace);
        }
    }
}

which generates the following output:

-------------------------------------
 Serializing StaticallyBuiltClass...
-------------------------------------
<?xml version="1.0" encoding="IBM437"?>
<StaticallyBuiltClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"&gt;
  <Values />
</StaticallyBuiltClass>
-------------------------------------
 Serializing DynamicallyBuiltClass...
-------------------------------------
Exception caught while serializing DynamicallyBuiltClass
There was an error generating the XML document.
Inner: The type initializer for 'Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationWriterDynamicallyBuiltClass' threw an exception.
Inner: Object reference not set to an instance of an object.
Inner: null

Stack trace:
   at System.Xml.Serialization.XmlSerializer.Serialize(XmlWriter xmlWriter, Object o, XmlSerializerNamespaces namespaces, String encodingStyle, String id)
   at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o, XmlSerializerNamespaces namespaces)
   at System.Xml.Serialization.XmlSerializer.Serialize(TextWriter textWriter, Object o)
   at Program.Serialize(Type type, Object o) in c:\dev\SerTest\SerTest\Program.cs:line 100

Edit: Removed some extraneous referenced assemblies

+2  A: 

Change CompilerParameters.GenerateInMemory to false and it will work. I don't know if this is a limitation of the XML serialization process, but if it's not a problem for you to generate the assembly to a temporary location on disk, then this will solve your problem.

João Angelo
Indeed, this worked. Have not accepted answer yet as I would like to know _why_ this happens.
Dr. Sbaitso
@Dr. Sbaitso, I'm also curious about the why. You can submit an issue in connect.microsoft.com in order to obtain an official explanation.
João Angelo