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">
<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
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.
RE: Change CompilerParameters.GenerateInMemory to false
And how is that done?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With