C# 使用XmlSerializer序列化多个类型的数组

C# 使用XmlSerializer序列化多个类型的数组,c#,xmlserializer,C#,Xmlserializer,我试图使用XMLSerializer生成如下XML,其中的内容是一个数组,但元素可以是不同的类型(在本例中为,和)。这可能吗 ... <create> <vendor> <vendorid>Unit - A-1212</vendorid> <name>this is the name8</name> <vcf_bill_s

我试图使用XMLSerializer生成如下XML,其中
的内容是一个数组,但元素可以是不同的类型(在本例中为
)。这可能吗

...
    <create>
        <vendor> 
            <vendorid>Unit - A-1212</vendorid>
            <name>this is the name8</name>
            <vcf_bill_siteid3>FOOBAR8</vcf_bill_siteid3>
        </vendor>             
        <customer>
            <CUSTOMERID>XML121</CUSTOMERID>
            <NAME>XML Customer 111</NAME>
        </customer>             
        <asset>  
            <createdAt>San Jose</createdAt>
            <createdBy>Kevin</createdBy>
            <serial_number>123456789</serial_number>
        </asset> 
    </create>
....
。。。
单位-A-1212
这是名字
FOOBAR8
XML121
XML客户111
圣何塞
凯文
123456789
....
在c#类中,只需确保始终返回可能返回的任何类型的空数组:

    [Serializable]
public class create
{
    public create()
    {
        vendor = new Vendor[0];
        customer = new Customer[0];
        asset = new Asset[0];
    }
    Vendor[] vendor { get; set; }
    Customer[] customer { get; set; }
    Asset[] asset { get; set; }
}
[Serializable]
public class Vendor
{
    public string vendorid { get; set; }
    public string name { get; set; }
    public string vcf_bill_siteid3 { get; set; }
}
[Serializable]
public class Customer
{
    public string CUSTOMERID { get; set; }
    public string NAME { get; set; }
}
[Serializable]
public class Asset
{
    public string createdAt { get; set; }
    public string createdBy { get; set; }
    public string serial_number { get; set; }
}

假设数组中所有可能的类型在编译时都是已知的,则可以为数组中可能出现的每个已知类型向数组应用多个属性。
Type
参数是数组中可能出现的特定派生类型,而
String
参数是与该类型关联的元素名称。还要将该属性应用于全局数组属性,以指定数组的名称,并将其序列化为两个级别,而不是一个级别

例如:

在哪里

(使用抽象基类型只是我的首选。您可以使用
对象作为基类型:
公共对象[]创建{get;set;}

更新

使用
XmlSerializer
序列化包含编译时未知的派生类型的多态集合非常困难,因为它通过动态代码生成工作。也就是说,当您第一次创建
XmlSerializer
时,它使用反射来编写c#代码来序列化和反序列化所有静态可发现的引用类型,然后将该代码编译并加载到动态DLL中以执行实际工作。不会为无法静态发现的类型创建代码,因此(反)序列化将失败

您有两个选项可以绕过此限制:

  • 在运行时发现列表中的所有派生类型,然后为多态数组属性构造,然后用发现的子类型填充数组属性的。然后将
    XmlAttributeOverrides
    传递给相应的服务器

    注意-必须在适当的哈希表中缓存并重用
    XmlSerializer
    ,否则将产生巨大的资源泄漏。看

    有关如何执行此操作的示例,请参见此处:

  • 在运行时发现列表中的所有派生类型,然后将其存储在实现的自定义
    list
    子类中

  • 由于必须缓存
    XmlSerializer
    这一麻烦,我倾向于第二种方法

    要查找所有派生类型,请执行以下操作:

    public static class TypeExtensions
    {
        public static IEnumerable<Type> DerivedTypes(this IEnumerable<Type> baseTypes)
        {
            var assemblies = baseTypes.SelectMany(t => t.Assembly.GetReferencingAssembliesAndSelf()).Distinct();
            return assemblies
                .SelectMany(a => a.GetTypes())
                .Where(t => baseTypes.Any(baseType => baseType.IsAssignableFrom(t)))
                .Distinct();
        }
    }
    
    public static class AssemblyExtensions
    {
        public static IEnumerable<Assembly> GetAllAssemblies()
        {
            // Adapted from 
            // https://stackoverflow.com/questions/851248/c-sharp-reflection-get-all-active-assemblies-in-a-solution
            return Assembly.GetEntryAssembly().GetAllReferencedAssemblies();
        }
    
        public static IEnumerable<Assembly> GetAllReferencedAssemblies(this Assembly root)
        {
            // WARNING: Assembly.GetAllReferencedAssemblies() will optimize away any reference if there
            // is not an explicit use of a type in that assembly from the referring assembly --
            // And simply adding an attribute like [XmlInclude(typeof(T))] seems not to do
            // the trick.  See
            // https://social.msdn.microsoft.com/Forums/vstudio/en-US/17f89058-5780-48c5-a43a-dbb4edab43ed/getreferencedassemblies-not-returning-complete-list?forum=netfxbcl
            // Thus if you are using this to, say, discover all derived types of a base type, the assembly
            // of the derived types MUST contain at least one type that is referenced explicitly from the 
            // root assembly, directly or indirectly.
    
            var list = new HashSet<string>();
            var stack = new Stack<Assembly>();
    
            stack.Push(root);
    
            do
            {
                var asm = stack.Pop();
    
                yield return asm;
    
                foreach (var reference in asm.GetReferencedAssemblies())
                    if (!list.Contains(reference.FullName))
                    {
                        stack.Push(Assembly.Load(reference));
                        list.Add(reference.FullName);
                    }
    
            }
            while (stack.Count > 0);
        }
    
        public static IEnumerable<Assembly> GetReferencingAssemblies(this Assembly target)
        {
            if (target == null)
                throw new ArgumentNullException();
            // Assemblies can have circular references:
            // http://stackoverflow.com/questions/1316518/how-did-microsoft-create-assemblies-that-have-circular-references
            // So a naive algorithm isn't going to work.
    
            var done = new HashSet<Assembly>();
    
            var root = Assembly.GetEntryAssembly();
            var allAssemblies = root.GetAllReferencedAssemblies().ToList();
    
            foreach (var assembly in GetAllAssemblies())
            {
                if (target == assembly)
                    continue;
                if (done.Contains(assembly))
                    continue;
                var refersTo = (assembly == root ? allAssemblies : assembly.GetAllReferencedAssemblies()).Contains(target);
                done.Add(assembly);
                if (refersTo)
                    yield return assembly;
            }
        }
    
        public static IEnumerable<Assembly> GetReferencingAssembliesAndSelf(this Assembly target)
        {
            return new[] { target }.Concat(target.GetReferencingAssemblies());
        }
    }
    
    公共静态类类型扩展
    {
    公共静态IEnumerable派生类型(此IEnumerable基类型)
    {
    var assemblies=baseTypes.SelectMany(t=>t.Assembly.getReferenceGassembliesAndSelf()).Distinct();
    返回组件
    .SelectMany(a=>a.GetTypes())
    .Where(t=>baseTypes.Any(baseType=>baseType.IsAssignableFrom(t)))
    .Distinct();
    }
    }
    公共静态类AssemblyExtensions
    {
    公共静态IEnumerable GetAllAssemblies()
    {
    //改编自
    // https://stackoverflow.com/questions/851248/c-sharp-reflection-get-all-active-assemblies-in-a-solution
    返回Assembly.GetEntryAssembly().getAllReferencedAssembly();
    }
    公共静态IEnumerable GetAllReferenceAssemblys(此程序集根)
    {
    //警告:Assembly.GetAllReferencedAssemblys()将优化掉任何引用(如果存在)
    //不是从引用程序集中显式使用该程序集中的类型--
    //简单地添加一个像[xmlclude(typeof(T))]这样的属性似乎不行
    //诀窍,看到了吗
    // https://social.msdn.microsoft.com/Forums/vstudio/en-US/17f89058-5780-48c5-a43a-dbb4edab43ed/getreferencedassemblies-not-returning-complete-list?forum=netfxbcl
    //因此,如果您使用它来发现基类型的所有派生类型,那么程序集
    //的派生类型必须至少包含一个从
    //根程序集,直接或间接。
    var list=newhashset();
    var stack=新堆栈();
    栈.推(根);
    做
    {
    var asm=stack.Pop();
    收益率;
    foreach(asm.getReferencedAssemblys()中的变量引用)
    如果(!list.Contains(reference.FullName))
    {
    堆栈.推送(组装.加载(参考));
    列表.添加(参考.全名);
    }
    }
    而(stack.Count>0);
    }
    公共静态IEnumerable GetReferenceGassemblies(此程序集目标)
    {
    if(target==null)
    抛出新ArgumentNullException();
    //程序集可以具有循环引用:
    // http://stackoverflow.com/questions/1316518/how-did-microsoft-create-assemblies-that-have-circular-references
    //所以一个简单的算法是行不通的。
    var done=new HashSet();
    var root=Assembly.GetEntryAssembly();
    var allAssemblies=root.getAllReferencedAssemblys().ToList();
    foreach(GetAllAssemblies()中的var程序集)
    {
    如果(目标==程序集)
    继续;
    如果(完成。包含(程序集))
    继续;
    var referesto=(assembly==根?allAssemblies:assembly.getAllReferencedAssemblys()).Contains(目标);
    完成。添加(组装);
    如果(参考)
    屈服返回组件;
    }
    }
    公共静态IEnumerable GetReferenceGassembliesAndSelf(此程序集目标)
    {
    返回新的[]{target}.Concat(target.getReferenceGasse
    
    public abstract class CreateBase
    {
    }
    
    public class Vendor : CreateBase
    {
        public string vendorid { get; set; }
        public string name { get; set; }
        public string vcf_bill_siteid3 { get; set; }
    }
    
    public class Customer : CreateBase
    {
        public string CUSTOMERID { get; set; }
        public string NAME { get; set; }
    }
    
    public class Asset : CreateBase
    {
        public string createdAt { get; set; }
        public string createdBy { get; set; }
        public string serial_number { get; set; }
    }
    
    public static class TypeExtensions
    {
        public static IEnumerable<Type> DerivedTypes(this IEnumerable<Type> baseTypes)
        {
            var assemblies = baseTypes.SelectMany(t => t.Assembly.GetReferencingAssembliesAndSelf()).Distinct();
            return assemblies
                .SelectMany(a => a.GetTypes())
                .Where(t => baseTypes.Any(baseType => baseType.IsAssignableFrom(t)))
                .Distinct();
        }
    }
    
    public static class AssemblyExtensions
    {
        public static IEnumerable<Assembly> GetAllAssemblies()
        {
            // Adapted from 
            // https://stackoverflow.com/questions/851248/c-sharp-reflection-get-all-active-assemblies-in-a-solution
            return Assembly.GetEntryAssembly().GetAllReferencedAssemblies();
        }
    
        public static IEnumerable<Assembly> GetAllReferencedAssemblies(this Assembly root)
        {
            // WARNING: Assembly.GetAllReferencedAssemblies() will optimize away any reference if there
            // is not an explicit use of a type in that assembly from the referring assembly --
            // And simply adding an attribute like [XmlInclude(typeof(T))] seems not to do
            // the trick.  See
            // https://social.msdn.microsoft.com/Forums/vstudio/en-US/17f89058-5780-48c5-a43a-dbb4edab43ed/getreferencedassemblies-not-returning-complete-list?forum=netfxbcl
            // Thus if you are using this to, say, discover all derived types of a base type, the assembly
            // of the derived types MUST contain at least one type that is referenced explicitly from the 
            // root assembly, directly or indirectly.
    
            var list = new HashSet<string>();
            var stack = new Stack<Assembly>();
    
            stack.Push(root);
    
            do
            {
                var asm = stack.Pop();
    
                yield return asm;
    
                foreach (var reference in asm.GetReferencedAssemblies())
                    if (!list.Contains(reference.FullName))
                    {
                        stack.Push(Assembly.Load(reference));
                        list.Add(reference.FullName);
                    }
    
            }
            while (stack.Count > 0);
        }
    
        public static IEnumerable<Assembly> GetReferencingAssemblies(this Assembly target)
        {
            if (target == null)
                throw new ArgumentNullException();
            // Assemblies can have circular references:
            // http://stackoverflow.com/questions/1316518/how-did-microsoft-create-assemblies-that-have-circular-references
            // So a naive algorithm isn't going to work.
    
            var done = new HashSet<Assembly>();
    
            var root = Assembly.GetEntryAssembly();
            var allAssemblies = root.GetAllReferencedAssemblies().ToList();
    
            foreach (var assembly in GetAllAssemblies())
            {
                if (target == assembly)
                    continue;
                if (done.Contains(assembly))
                    continue;
                var refersTo = (assembly == root ? allAssemblies : assembly.GetAllReferencedAssemblies()).Contains(target);
                done.Add(assembly);
                if (refersTo)
                    yield return assembly;
            }
        }
    
        public static IEnumerable<Assembly> GetReferencingAssembliesAndSelf(this Assembly target)
        {
            return new[] { target }.Concat(target.GetReferencingAssemblies());
        }
    }
    
    public class XmlPolymorphicList<T> : List<T>, IXmlSerializable where T : class
    {
        static XmlPolymorphicList()
        {
            // Make sure the scope of objects to find isn't *EVERYTHING*
            if (typeof(T) == typeof(object))
            {
                throw new InvalidOperationException("Cannot create a XmlPolymorphicList<object>");
            }
        }
    
        internal sealed class DerivedTypeDictionary
        {
            Dictionary<Type, string> derivedTypeNames;
            Dictionary<string, Type> derivedTypes;
    
            DerivedTypeDictionary()
            {
                derivedTypeNames = typeof(T).DerivedTypes().ToDictionary(t => t, t => t.DefaultXmlElementName());
                derivedTypes = derivedTypeNames.ToDictionary(p => p.Value, p => p.Key); // Will throw an exception if names are not unique
            }
    
            public static DerivedTypeDictionary Instance { get { return Singleton<DerivedTypeDictionary>.Instance; } }
    
            public string GetName(Type type)
            {
                return derivedTypeNames[type];
            }
    
            public Type GetType(string name)
            {
                return derivedTypes[name];
            }
        }
    
        public XmlPolymorphicList()
            : base()
        {
        }
    
        public XmlPolymorphicList(IEnumerable<T> items)
            : base(items)
        {
        }
    
        #region IXmlSerializable Members
    
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }
    
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            reader.ReadStartElement();
            while (reader.NodeType == XmlNodeType.Element)
            {
                var name = reader.Name;
                var type = DerivedTypeDictionary.Instance.GetType(name);
                var item = (T)(new XmlSerializer(type).Deserialize(reader));
                if (item != null)
                    Add(item);
            }
        }
    
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            foreach (var item in this)
            {
                new XmlSerializer(item.GetType()).Serialize(writer, item);
            }
        }
    
        #endregion
    }
    
    public static class XmlSerializationHelper
    {
        public static string DefaultXmlElementName(this Type type)
        {
            var xmlType = type.GetCustomAttribute<XmlTypeAttribute>();
            if (xmlType != null && !string.IsNullOrEmpty(xmlType.TypeName))
                return xmlType.TypeName;
            return type.Name;
        }
    }
    
    public class Singleton<T> where T : class
    {
        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static Singleton()
        {
        }
    
        /// <summary>
        /// Private nested class which acts as singleton class instantiator. This class should not be accessible outside <see cref="Singleton<T>"/>
        /// </summary>
        class Nested
        {
            /// <summary>
            /// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit
            /// </summary>
            static Nested()
            {
            }
    
            /// <summary>
            /// Static instance variable
            /// </summary>
            internal static readonly T instance = (T)Activator.CreateInstance(typeof(T), true);
        }
    
        public static T Instance { get { return Nested.instance; } }
    }