如何使用XmlSerializer序列化内部类?

我正在建立一个与第三方接口的库。 通过XML和HTTPpost进行通信。 那很有效。

但是,无论代码使用哪个库都不需要知道内部类。 我使用此方法将内部对象序列化为XML:

internal static string SerializeXML(Object obj) { XmlSerializer serializer = new XmlSerializer(obj.GetType(), "some.domain"); //settings XmlWriterSettings settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; using (StringWriter stream = new StringWriter()) { using (XmlWriter writer = XmlWriter.Create(stream, settings)) { serializer.Serialize(writer, obj); } return stream.ToString(); } } 

但是,当我将类的访问修饰符更改为internal ,我在运行时得到一个exception:

[System.InvalidOperationException] = {“MyNamespace.MyClass由于其保护级别而无法访问。只能处理公共类型。”}

该exception发生在上面代码的第一行。

我希望我的图书馆的课程不公开,因为我不想公开它们。 我能这样做吗? 如何使用我的通用序列化程序使内部类型可序列化? 我究竟做错了什么?

来自Sowmy Srinivasan的博客 – 使用XmlSerializer序列化内部类型 :

能够序列化内部类型是XmlSerializer团队看到的常见请求之一。 这是人们运送图书馆的合理要求。 他们不希望仅为了序列化器而使XmlSerializer类型公开。 我最近从编写XmlSerializer的团队搬到了一个使用XmlSerializer的团队。 当我遇到类似的请求时,我说:“没办法。使用DataContractSerializer ”。

原因很简单。 XmlSerializer通过生成代码来工作。 生成的代码存在于动态生成的程序集中,需要访问要序列化的类型。 由于XmlSerializer是在轻量级代码生成出现之前的一段时间内开发的,因此生成的代码无法访问除另一个程序集中的公共类型之外的任何内容。 因此,序列化的类型必须是公开的。

我听到精明的读者低声说道“如果使用’ InternalsVisibleTo ‘属性,则不必公开”。

我说,“没错,但是生成的组件的名称不是预先知道的。你可以将内部组件看到哪个组件?”

精明的读者:“如果使用’ sgen.exe ‘,则会知道程序集名称”

我:“为了让sgen为你的类型生成序列化器,它们必须是公开的”

精明的读者:“我们可以进行两次传递编辑。一个传球用于sgen,类型为公共,另一传球用于传送类型作为内部传递。”

他们可能是对的! 如果我让精明的读者给我写一个样本,他们可能会写这样的东西。 (免责声明:这不是官方解决方案.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; } } 

一些精明的“黑客”读者可能会给我build.cmd来编译它。

 csc /d:Pass1 program.cs sgen program.exe csc program.cs 

一种解决方法是使用DataContractSerializer而不是XmlSerializer。

另见: http : //blogs.msdn.com/b/sowmy/archive/2008/10/04/serializing-internal-types-using-xmlserializer.aspx

作为替代方案,您可以使用动态创建的公共类(不会向第三方公开):

 static void Main() { var emailType = CreateEmailType(); dynamic email = Activator.CreateInstance(emailType); email.From = "x@xpto.com"; email.To = "y@acme.com"; email.Subject = "Dynamic Type"; email.Boby = "XmlSerializer can use this!"; } static Type CreateEmailType() { var assemblyName = new AssemblyName("DynamicAssembly"); var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); var moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name); var typeBuilder = moduleBuilder.DefineType( "Email", ( TypeAttributes.Public | TypeAttributes.Sealed | TypeAttributes.SequentialLayout | TypeAttributes.Serializable ), typeof(ValueType) ); typeBuilder.DefineField("From", typeof(string), FieldAttributes.Public); typeBuilder.DefineField("To", typeof(string), FieldAttributes.Public); typeBuilder.DefineField("Subject", typeof(string), FieldAttributes.Public); typeBuilder.DefineField("Body", typeof(string), FieldAttributes.Public); return typeBuilder.CreateType(); } 

这可能对您有所帮助: MRB_ObjectSaver

此项目可帮助您将c#中的任何对象保存/加载/克隆到文件/字符串中。 与“c#serialization”相比,此方法保持对对象的引用,对象之间的链接不会中断。 (参见:SerializeObjectTest.cs示例)此外,该类型未标记为[Serializable]

您还可以使用名为xgenplus(http://xge​​nplus.codeplex.com/)的内容生成通常在运行时执行的代码。 然后将其添加到您的解决方案中,并将其作为解决方案的一部分进行编译。 此时,如果您的对象是内部对象并不重要 – 您可以在同一名称空间中添加预生成的代码。 由于其全部预先生成,因此其性能非常快。