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

2019-05-12 07:34发布

我建立与第三方接口库。 通信是通过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();
    }
}

然而,当我改变我的班访问修饰符到internal ,我会在运行时异常:

[System.InvalidOperationException] = {“MyNamespace.MyClass是不可访问的,因为它保护级别。只有公共类型都可以加工。”}

这发生异常上面代码中的第一行。

我想我的库中的类不被公开,因为我不想揭露他们。 我能做到这一点? 我怎样才能使内部类型的序列化,用我的通用串行? 我究竟做错了什么?

Answer 1:

从Sowmy斯里尼瓦桑的博客-采用序列化XmlSerializer的内部类型 :

如果能够序列化的内部类型是由看到的常见请求之一XmlSerializer的团队。 它是从人的出货库的合理要求。 他们不想让XmlSerializer的各类公共只是串行的缘故。 最近,我从写XmlSerializer的一个团队,消耗XmlSerializer的球队搬迁。 当我遇到了类似的请求来到我说:“没办法,使用DataContractSerializer的 ”。

原因很简单。 XmlSerializer的工作原理是生成代码。 所生成的代码在动态生成的组件和需要访问被序列化的类型。 由于XmlSerializer的是在一个时间出现之前开发的轻量级代码生成 ,生成的代码不能访问除公共类型在另一个装配的任何其他。 因此,正在连载的类型必须是公开的。

我听说聪明的读者耳语“它没有如果‘是公共InternalsVisibleTo使用’属性”。

我说,“是的,但生成的程序集的名称是不知道的前期,为了组装才能让该内部可见?”

聪明的读者:“如果使用‘程序集的名称被称为sgen.exe ’”

我说:“对于SGEN为您类型生成序列化,他们必须公开”

聪明的读者:“我们可以与各类公共和另一通与类型,内部出货SGEN做两遍汇编一通。”

他们可能是对的! 如果我问精明的读者给我写的样本,他们可能会写这样的事情。 (免责声明:这不是官方的解决方案因人而异)

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


Answer 2:

一个解决办法是使用DataContractSerializer的,而不是一个XmlSerializer的。

也看到这一点: http://blogs.msdn.com/b/sowmy/archive/2008/10/04/serializing-internal-types-using-xmlserializer.aspx



Answer 3:

作为替代,您可以使用动态创建公共类(不会被暴露给第三方):

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();
}


Answer 4:

这可以帮助你: MRB_ObjectSaver

这个项目可以帮助您节省/负载/从一个文件/字符串克隆在C#中的任何对象/。 在比较“C#序列化”这种方法保持引用对象不会打破之间的对象和纽带。 (参见:SerializeObjectTest.cs的例子)此外,类型还没有被标记为[序列化]



Answer 5:

您还可以使用一种叫做xgenplus(http://xge​​nplus.codeplex.com/)这会产生,通常会获得在运行时执行的代码。 然后,您添加到您的解决方案,并将其编译为您的解决方案的一部分。 在这一点上,它并不重要,如果你的对象是内部的 - 你可以在同一个名称空间中添加预生成的代码。 这样做的性能极快,它与所有预先生成。



文章来源: How can I serialize internal classes using XmlSerializer?