/// /// 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:
listentry
如您所见, 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" }) }), } };