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

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

 ...   Unit - A-1212 this is the name8 FOOBAR8   XML121 XML Customer 111   San Jose Kevin 123456789   .... 

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

例如:

 public class Document { [XmlArray("create")] [XmlArrayItem("vendor", typeof(Vendor))] [XmlArrayItem("customer", typeof(Customer))] [XmlArrayItem("asset", typeof(Asset))] public CreateBase [] Create { get; set; } } 

哪里

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

(使用抽象基类型只是我的偏好。您可以使用object作为基类型: public object [] Create { get; set; }

更新

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

您有两种方法可以解决此限制:

  1. 在运行时查找列表中的所有派生类型,然后构造XmlAttributeOverrides ,为多态数组属性添加XmlAttributes ,然后使用发现的子类型填充数组属性的XmlArrayItems 。 然后将XmlAttributeOverrides传递给适当的XmlSerializer constructor

    注意 – 您必须在适当的哈希表中缓存和重用XmlSerializer ,否则您将遇到巨大的资源泄漏。 看到这里 。

    有关如何执行此操作的示例,请参阅此处: 强制XmlDefaultValue值的XML序列化 。

  2. 在运行时查找列表中的所有派生类型,然后将其存储在实现IXmlSerializable的自定义List子类中。

由于必须缓存XmlSerializer的麻烦,我倾向于第二种方法。

要发现所有派生类型:

 public static class TypeExtensions { public static IEnumerable DerivedTypes(this IEnumerable 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 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 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(); var stack = new Stack(); 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 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(); 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 GetReferencingAssembliesAndSelf(this Assembly target) { return new[] { target }.Concat(target.GetReferencingAssemblies()); } } 

然后,发现自己的类型的多态列表:

 public class XmlPolymorphicList : List, 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"); } } internal sealed class DerivedTypeDictionary { Dictionary derivedTypeNames; Dictionary 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.Instance; } } public string GetName(Type type) { return derivedTypeNames[type]; } public Type GetType(string name) { return derivedTypes[name]; } } public XmlPolymorphicList() : base() { } public XmlPolymorphicList(IEnumerable 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(); if (xmlType != null && !string.IsNullOrEmpty(xmlType.TypeName)) return xmlType.TypeName; return type.Name; } } public class Singleton where T : class { // Explicit static constructor to tell C# compiler // not to mark type as beforefieldinit static Singleton() { } ///  /// Private nested class which acts as singleton class instantiator. This class should not be accessible outside  ///  class Nested { ///  /// Explicit static constructor to tell C# compiler not to mark type as beforefieldinit ///  static Nested() { } ///  /// Static instance variable ///  internal static readonly T instance = (T)Activator.CreateInstance(typeof(T), true); } public static T Instance { get { return Nested.instance; } } } 

在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; } } 

我最终放弃了XmlSerializer并使用Json.NET将对象序列化为json,然后转换为XML。