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

标签 c# xml oop serialization xml-serialization

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

但是,无论使用该库的代码都不需要知道内部类。我的内部对象使用此方法序列化为 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 is inaccessible due to its protection level. Only public types can be processed."}

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

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

最佳答案

来自 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

关于c# - 如何使用 XmlSerializer 序列化内部类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6156692/

相关文章:

Java - 将类的引用传递给另一个类

c# - 使用 C# Regex 替换字符串中的字符

c# - 带有 IIS Express 的 VS2017 未显示最新代码更改

java - jaxb编码 self 关系

c++ - RapidXML 访问兄弟节点似乎无缘无故地导致段错误

c# - 重构单例过度使用

oop - 扩展 DAO 的范围

c# - 为什么突然一个线程运行一个方法的一部分?

c# - 语音识别从音频文件而不是麦克风进行识别

arrays - 如何从 XML 文件在 Odoo 14 中添加数组