Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Being able to serialize internal types is one of the common requests seen by the XmlSerializer team. It is a reasonable request from people shipping libraries. They do not want to make the XmlSerializer types public just for the sake of the serializer. I recently moved from the team that wrote the XmlSerializer to a team that consumes XmlSerializer. When I came across a similar request I said, "No way. Use DataContractSerializer".
The reason is simple. XmlSerializer works by generating code. The generated code lives in a dynamically generated assembly and needs to access the types being serialized. Since XmlSerializer was developed in a time before the advent of lightweight code generation, the generated code cannot access anything other than public types in another assembly. Hence the types being serialized has to be public.
I hear astute readers whisper "It does not have to be public if 'InternalsVisibleTo' attribute is used".
I say, "Right, but the name of the generated assembly is not known upfront. To which assembly do you make the internals visible to?"
Astute readers : "the assembly name is known if one uses 'sgen.exe'"
Me: "For sgen to generate serializer for your types, they have to be public"
Astute readers : "We could do a two pass compilation. One pass for sgen with types as public and another pass for shipping with types as internals."
They may be right! If I ask the astute readers to write me a sample they would probably write something like this. (Disclaimer: This is not the official solution. YMMV)
using System;
using System.IO;
using System.Xml.Serialization;
using System.Runtime.CompilerServices;
using System.Reflection;
[assembly: InternalsVisibleTo("Program.XmlSerializers")]
namespace InternalTypesInXmlSerializer
{
class Program
{
static void Main(string[] args)
{
Address address = new Address();
address.Street = "One Microsoft Way";
address.City = "Redmond";
address.Zip = 98053;
Order order = new Order();
order.BillTo = address;
order.ShipTo = address;
XmlSerializer xmlSerializer = GetSerializer(typeof(Order));
xmlSerializer.Serialize(Console.Out, order);
}
static XmlSerializer GetSerializer(Type type)
{
#if Pass1
return new XmlSerializer(type);
#else
Assembly serializersDll = Assembly.Load("Program.XmlSerializers");
Type xmlSerializerFactoryType = serializersDll.GetType("Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract");
MethodInfo getSerializerMethod = xmlSerializerFactoryType.GetMethod("GetSerializer", BindingFlags.Public | BindingFlags.Instance);
return (XmlSerializer)getSerializerMethod.Invoke(Activator.CreateInstance(xmlSerializerFactoryType), new object[] { type });
#endif
}
}
#if Pass1
public class Address
#else
internal class Address
#endif
{
public string Street;
public string City;
public int Zip;
}
#if Pass1
public class Order
#else
internal class Order
#endif
{
public Address ShipTo;
public Address BillTo;
}
}
Some astute 'hacking' readers may go as far as giving me the build.cmd to compile it.
csc /d:Pass1 program.cs
sgen program.exe
csc program.cs
Comments
- Anonymous
October 04, 2008
PingBack from http://www.easycoded.com/serializing-internal-types-using-xmlserializer/