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

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

问题描述

我正在构建一个库来与第三方交互.通信是通过 XML 和 HTTP Posts 进行的.那是有效的.

但是,任何使用库的代码都不需要知道内部类.我的内部对象使用此方法序列化为 XML:

内部静态字符串SerializeXML(Object obj){XmlSerializer 序列化器 = new XmlSerializer(obj.GetType(), "some.domain");//设置XmlWriterSettings settings = new XmlWriterSettings();settings.Indent = true;settings.OmitXmlDeclaration = true;使用 (StringWriter stream = new StringWriter()){使用 (XmlWriter writer = XmlWriter.Create(stream, settings)){serializer.Serialize(writer, obj);}返回流.ToString();}}

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

<块引用>

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

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

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

解决方案

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

<块引用>

能够序列化内部类型是常见的请求之一由 XmlSerializer 团队看到.这是人们的合理要求运送图书馆.他们不想让 XmlSerializer 类型公开只是为了序列化程序.我最近从将 XmlSerializer 写入使用的团队的团队XmlSerializer.当我遇到类似的要求时,我说:不可能.使用 DataContractSerializer".

原因很简单.XmlSerializer 通过生成代码来工作.这生成的代码存在于动态生成的程序集中,需要访问被序列化的类型.由于 XmlSerializer 被开发在 轻量级出现之前的时间代码生成,生成的代码不能访问公共类型以外的任何东西另一个大会.因此被序列化的类型必须是公开的.

我听到精明的读者低声说如果'InternalsVisibleTo 属性被使用".

我说,对,但是生成的程序集的名称未知前期.你让哪个组件可以看到内部结构?"

精明的读者:如果使用 'sgen.exe'"

我:要让 sgen 为您的类型生成序列化程序,它们必须是公开"

精明的读者:我们可以做两遍编译.一遍为sgen 类型为 public 和另一个传递类型为内部结构."

他们可能是对的!如果我请精明的读者给我写一个样本他们可能会写这样的东西.(免责声明:这是不是官方解决方案.YMMV)

使用系统;使用 System.IO;使用 System.Xml.Serialization;使用 System.Runtime.CompilerServices;使用 System.Reflection;[程序集:InternalsVisibleTo("Program.XmlSerializers")]命名空间 InternalTypesInXmlSerializer{课程计划{静态无效主(字符串 [] args){地址 address = new Address();address.Street = "一种微软方式";address.City = "雷德蒙德";地址.Zip = 98053;订单订单=新订单();order.BillTo = 地址;order.ShipTo = 地址;XmlSerializer xmlSerializer = GetSerializer(typeof(Order));xmlSerializer.Serialize(Console.Out, order);}静态 XmlSerializer GetSerializer(类型类型){#if Pass1返回新的 XmlSerializer(类型);#别的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 });#万一}}#if Pass1公开课地址#别的内部类地址#万一{公串街;公串市;公共国际邮编;}#if Pass1公开课秩序#别的内部类订单#万一{公共地址 ShipTo;公共地址 BillTo;}}

<块引用>

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

csc/d:Pass1 program.cssgen程序.execsc程序.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天全站免登陆