XmlInclude:列表和数组

我有一个对象,它有变量作为object ,我想用XML序列化它。

为此,我添加了一些XmlInclude属性,以便管理可以使用的所有类型。

 [Serializable] [XmlInclude(typeof(short[]))] [XmlInclude(typeof(ushort[]))] [XmlInclude(typeof(int[]))] [XmlInclude(typeof(uint[]))] [XmlInclude(typeof(ulong[]))] [XmlInclude(typeof(long[]))] [XmlInclude(typeof(byte[]))] [XmlInclude(typeof(decimal[]))] [XmlInclude(typeof(float[]))] [XmlInclude(typeof(double[]))] [XmlInclude(typeof(string[]))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(MyObject))] [XmlInclude(typeof(TimeSpan))] [XmlInclude(typeof(OtherObject))] [XmlInclude(typeof(MySubObject1))] [XmlInclude(typeof(MySubObject2))] [XmlRoot(ElementName = "mc")] public class MyClass: IComparable { [XmlElement("fm")] public object FirstMember; [XmlElement("sm")] public object SecondMember; [XmlElement("tm")] public object ThirdMember; } 

我的问题是数组和列表声明不共存。

奇怪的是,如果首先放置数组属性,则数组成员被正确序列化,但不是列表成员。 反之亦然。

自定义类和派生类可以正常工作,但ListArray不能。 我只能找到类的例子,但我使用原始类型。

有没有人有想法?

PS:我知道我的post与此类似,但自2011年以来没有答案。

我可以重现这个问题。 这是因为XmlSerializer为数组和列表(在这种情况下为字符串)生成相同的XML:

   list entry   

由于序列化程序对string[]List使用相同的xsi:type多态名称"ArrayOfString" ,当它找到可能遇到两者的情况时,它会抛出exception,因为它无法区分它们。

为什么XmlSerializer对两者都使用相同的名称? 我只能猜测它能够交换通过序列化不同集合类型(例如,从ListSortedSet )创建的XML,而无需修复XML文件格式。

但是,在您的情况下,您需要在XML中区分这些类型的集合,而不是交换它们。 因此,您将需要创建某种包装类,以允许在文件中区分它们。

例如,考虑以下类的简化:

 [XmlInclude(typeof(string))] [XmlInclude(typeof(string[]))] [XmlInclude(typeof(object[]))] [XmlInclude(typeof(List))] [XmlInclude(typeof(List))] [XmlInclude(typeof(SortedSet))] [XmlInclude(typeof(SortedSet))] [XmlInclude(typeof(HashSet))] [XmlInclude(typeof(HashSet))] [XmlInclude(typeof(LinkedList))] [XmlInclude(typeof(LinkedList))] [XmlRoot(ElementName = "mc")] public class MyClass { [XmlElement("fm")] public object FirstMember; } 

这里FirstMember可以包含字符串,字符串或对象的数组,或各种类型的字符串或对象的集合。

要为各种类型的集合建立不同的xsi:type值,可以引入以下通用包装器类型:

 ///  /// Abstract base type for a generic collection wrapper where, to differentiate /// between arrays and lists and other types of collections of the same underlying /// item type, it is necessary to introduce an intermediary type to establish /// distinct xsi:type values. ///  public abstract class CollectionWrapper { [XmlIgnore] public abstract IEnumerable RealCollection { get; } static bool TryCreateWrapperType(Type actualType, out Type wrapperType) { if (actualType.IsArray || actualType.IsPrimitive || actualType == typeof(string) || !typeof(IEnumerable).IsAssignableFrom(actualType) || actualType == typeof(TElement) // Not polymorphic || !actualType.IsGenericType) { wrapperType = null; return false; } var args = actualType.GetGenericArguments(); if (args.Length != 1) { wrapperType = null; return false; } if (actualType.GetGenericTypeDefinition() == typeof(List<>)) { wrapperType = typeof(ListWrapper<>).MakeGenericType(args); } else if (actualType.GetGenericTypeDefinition() == typeof(HashSet<>)) { wrapperType = typeof(HashSetWrapper<>).MakeGenericType(args); } else if (actualType.GetGenericTypeDefinition() == typeof(SortedSet<>)) { wrapperType = typeof(SortedSetWrapper<>).MakeGenericType(args); } else { var collectionTypes = actualType.GetCollectionItemTypes().ToList(); if (collectionTypes.SequenceEqual(args)) wrapperType = typeof(CollectionWrapper<,>).MakeGenericType(new [] { actualType, args[0] }); else { wrapperType = null; return false; } } if (!typeof(TElement).IsAssignableFrom(wrapperType)) { wrapperType = null; return false; } return true; } public static TElement Wrap(TElement item) { if (item == null) return item; var type = item.GetType(); if (type == typeof(TElement)) return item; Type wrapperType; if (!TryCreateWrapperType(type, out wrapperType)) return item; return (TElement)Activator.CreateInstance(wrapperType, item); } public static TElement Unwrap(TElement item) { if (item is CollectionWrapper) return (TElement)((CollectionWrapper)(object)item).RealCollection; return item; } } ///  /// Generic wrapper type for a generic collection of items. ///  ///  ///  public class CollectionWrapper : CollectionWrapper where TCollection : ICollection, new() { public class CollectionWrapperEnumerable : IEnumerable { readonly TCollection collection; public CollectionWrapperEnumerable(TCollection collection) { this.collection = collection; } public void Add(TElement item) { collection.Add(CollectionWrapper.Unwrap(item)); } #region IEnumerable Members public IEnumerator GetEnumerator() { foreach (var item in collection) yield return CollectionWrapper.Wrap(item); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion } readonly TCollection collection; readonly CollectionWrapperEnumerable enumerable; public CollectionWrapper() : this(new TCollection()) { } public CollectionWrapper(TCollection collection) { if (collection == null) throw new ArgumentNullException(); this.collection = collection; this.enumerable = new CollectionWrapperEnumerable(collection); } [XmlElement("Item")] public CollectionWrapperEnumerable SerializableEnumerable { get { return enumerable; } } [XmlIgnore] public override IEnumerable RealCollection { get { return collection; } } } // These three subclasses of CollectionWrapper for commonly encounterd collections were introduced to improve readability public class ListWrapper : CollectionWrapper, TElement> { public ListWrapper() : base() { } public ListWrapper(List list) : base(list) { } } public class HashSetWrapper : CollectionWrapper, TElement> { public HashSetWrapper() : base() { } public HashSetWrapper(HashSet list) : base(list) { } } public class SortedSetWrapper : CollectionWrapper, TElement> { public SortedSetWrapper() : base() { } public SortedSetWrapper(SortedSet list) : base(list) { } } public static class TypeExtensions { ///  /// Return all interfaces implemented by the incoming type as well as the type itself if it is an interface. ///  ///  ///  public static IEnumerable GetInterfacesAndSelf(this Type type) { if (type == null) throw new ArgumentNullException(); if (type.IsInterface) return new[] { type }.Concat(type.GetInterfaces()); else return type.GetInterfaces(); } public static IEnumerable GetCollectionItemTypes(this Type type) { foreach (Type intType in type.GetInterfacesAndSelf()) { if (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) { yield return intType.GetGenericArguments()[0]; } } } } 

然后在您的简化类中使用如下:

 [XmlInclude(typeof(string))] [XmlInclude(typeof(string[]))] [XmlInclude(typeof(object[]))] [XmlInclude(typeof(ListWrapper))] [XmlInclude(typeof(ListWrapper))] [XmlInclude(typeof(SortedSetWrapper))] [XmlInclude(typeof(SortedSetWrapper))] [XmlInclude(typeof(HashSetWrapper))] [XmlInclude(typeof(HashSetWrapper))] [XmlInclude(typeof(CollectionWrapper, string>))] [XmlInclude(typeof(CollectionWrapper, object>))] [XmlRoot(ElementName = "mc")] public class MyClass { [XmlElement("fm")] [JsonIgnore] public object XmlFirstMember { get { return CollectionWrapper.Wrap(FirstMember); } set { FirstMember = CollectionWrapper.Unwrap(value); } } [XmlIgnore] public object FirstMember; } 

然后,为一个简单的字符串列表:

 var myClass = new MyClass { FirstMember = new List { "list", "entry" } }; 

生成以下XML:

   list entry   

如您所见, xsi:type现在是不同的。

如果我创建以下更复杂的对象:

 var myClass = new MyClass { FirstMember = new List { new List { new List { new List { "hello" } }, "there" }, new HashSet { "hello", "hello", "there" }, new SortedSet { "hello", "hello", "there" }, new LinkedList( new object [] { new LinkedList( new [] { "hello", "there" }) }), } }; 

生成以下XML:

      hello   there   hello there   hello there    hello there     

每个不同的集合类型如何拥有自己独特的xsi:type