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

Adr*_*iro 28 c# xml oop serialization xml-serialization

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

但是,无论代码使用哪个库都不需要知道内部类.我使用此方法将内部对象序列化为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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

该异常发生在上面代码的第一行.

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

hem*_*emp 21

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

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

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

我听到精明的读者低声说道"如果使用' InternalsVisibleTo '属性,就不必公开".

我说,"没错,但是生成的组件的名称并不是预先知道的.你可以将内部组件显示给哪个组件?"

精明的读者:"如果使用' sgen.exe ' ,程序集名称是已知的"

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

精明的读者:"我们可以进行两次传递编辑.一次传递sgen,类型为public,另一次传递,传输类型为内部."

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

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

csc /d:Pass1 program.cs

sgen program.exe

csc program.cs
Run Code Online (Sandbox Code Playgroud)