我如何使用XmlSerializer的序列化内部类? [英] How can I serialize internal classes using XmlSerializer?

查看:266
本文介绍了我如何使用XmlSerializer的序列化内部类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要建一个第三方接口库。通信是通过XML和HTTP的帖子。这是工作。

但是,无论code使用该库并不需要知道内部类。我的内部对象使用此方法序列化到XML:

 内部静态字符串SerializeXML(obj对象)
{
    XmlSerializer的串行=新的XmlSerializer(obj.GetType(),some.domain);    //设置
    XmlWriterSettings设置=新XmlWriterSettings();
    settings.Indent = TRUE;
    settings.OmitXmlDeclaration = TRUE;    使用(StringWriter的流=新的StringWriter())
    {
        使用(XmlWriter的作家= XmlWriter.Create(流设置))
        {
            serializer.Serialize(作家,OBJ);
        }
        返回stream.ToString();
    }
}

然而,当我改变我的班的访问修饰符内部,我得到一个异常的运行


  

[System.InvalidOperationException] = {MyNamespace.MyClass不可访问由于其保护水平,只有公共类型都可以加工。}


这是例外发生在code以上的第一行。

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


解决方案

从<一个href=\"http://blogs.msdn.com/b/sowmy/archive/2008/10/04/serializing-internal-types-using-xmlserializer.aspx\">Sowmy斯里尼瓦桑的博客 - 采用序列化XmlSerializer的内部类型:


  

如果能够序列化的内部类型是常见的要求之一
  由<一看到href=\"http://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.aspx\">XmlSerializer球队。它是从人的合理要求
  航运库。他们不想让XmlSerializer的类型
  公众只是为串行器的缘故。我最近从移动
  团队写了XmlSerializer的一个团队,消耗
  XmlSerializer的。当我遇到了类似的请求,来到我说:没办法。
  使用<一个href=\"http://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx\">DataContractSerializer\".


  
  

原因很简单。 XmlSerializer的工作原理是code。该
  产生code生活在一个动态生成的程序集,需要
  正在连载访问类型。由于XmlSerializer的开发
  在<一来临之前的时间href=\"http://msdn.microsoft.com/en-us/library/system.reflection.emit.dynamicmethod(VS.80).aspx\">lightweight code一代中,
  产生code不能比公共类型的访问的任何其他
  另一个装配。因此,被序列化的类型必须是公开的。


  
  

我听到敏锐的读者私聊这并不一定是公开的,如果
  <一个href=\"http://msdn.microsoft.com/en-us/library/system.runtime.compilerservices.internalsvisibletoattribute.aspx\">InternalsVisibleTo'属性是用来。


  
  

我说:是的,但生成的程序集的名称是未知
  前期。到装配做才能让你的内部可见?


  
  

细心的读者:若用'程序集的名称被称为 sgen.exe '


  
  

我说:对于SGEN为您类型生成序列化,他们必须
  大众


  
  

细心的读者:我们可以做一个双向编译一个通行证。
  同类型的公共和另一路经SGEN与类型作为航运
  内部。


  
  

他们可能是对的!如果我问精明的读者给我写一个样本
  他们可能会这样写。 (声明:本网站
  没有官方的解决办法。因人而异)


 使用系统;
使用System.IO;
使用的System.Xml.Serialization;
使用System.Runtime.CompilerServices;
使用的System.Reflection;[大会:InternalsVisibleTo(Program.XmlSerializers)]命名空间InternalTypesInXmlSerializer
{
    类节目
    {
        静态无效的主要(字串[] args)
        {
            地址地址=新地址();
            address.Street =一个Microsoft方式;
            address.City =雷蒙德
            address.Zip = 98053;
            订购订购=新订单();
            order.BillTo =地址;
            order.ShipTo =地址;            XmlSerializer的XmlSerializer的= GetSerializer(typeof运算(顺序));
            xmlSerializer.Serialize(Console.Out,顺序);
        }        静态XmlSerializer的GetSerializer(类型类型)
        {
#如果P​​ASS1
            返回新的XmlSerializer(类型);
#其他
            装配serializersDll =的Assembly.Load(Program.XmlSerializers);
            键入xmlSerializerFactoryType = serializersDll.GetType(Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializerContract);            MethodInfo的getSerializerMethod = xmlSerializerFactoryType.GetMethod(GetSerializer,BindingFlags.Public | BindingFlags.Instance);            返回(XmlSerializer的)getSerializerMethod.Invoke(Activator.CreateInstance(xmlSerializerFactoryType),新的对象[] {型});#万一
        }
    }#如果P​​ASS1
    公共类地址
#其他
    内部类地址
#万一
    {
        公共字符串街;
        公共字符串城;
        公众诠释邮编;
    }#如果P​​ASS1
    公共类订单
#其他
    内部类订单
#万一
    {
        公共广播SHIPTO;
        公共地址收单方;
    }
}


  

一些精明的'黑客'的读者可能会去尽可能给我build.cmd
  编译它。


  CSC / D:PASS1 Program.cs中SGEN的Program.exeCSC的Program.cs

I'm building a library to interface with a third party. Communication is through XML and HTTP Posts. That's working.

But, whatever code uses the library does not need to be aware of the internal classes. My internal objects are serialized to XML using this method:

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

However, when I change my classes' access modifier to internal, I get an exception at runtime:

[System.InvalidOperationException] = {"MyNamespace.MyClass is inaccessible due to its protection level. Only public types can be processed."}

That exception happens in the first line of the code above.

I would like my library's classes not to be public because I do not want to expose them. Can I do that? How can I make internal types serializable, using my generic serializer? What am I doing wrong?

解决方案

From Sowmy Srinivasan's Blog - Serializing internal types using XmlSerializer:

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

这篇关于我如何使用XmlSerializer的序列化内部类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆