我有一个变量为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<short>))]
[XmlInclude(typeof(List<ushort>))]
[XmlInclude(typeof(List<int>))]
[XmlInclude(typeof(List<uint>))]
[XmlInclude(typeof(List<long>))]
[XmlInclude(typeof(List<ulong>))]
[XmlInclude(typeof(List<byte>))]
[XmlInclude(typeof(List<decimal>))]
[XmlInclude(typeof(List<float>))]
[XmlInclude(typeof(List<double>))]
[XmlInclude(typeof(List<string>))]
[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;
}
奇怪的是,如果首先放置数组属性,则数组成员被正确序列化,但不是列表成员。反之亦然。
自定义类和派生类可以正常工作,但List
和Array
并不适用。我只能找到类的例子,但我使用原始类型。
有没有人有想法?
P.S。:我知道我的帖子与this one相似,但自2011年以来没有答案。
答案 0 :(得分:1)
我可以重现这个问题。之所以会发生这种情况是因为XmlSerializer
为数组和列表(在这种情况下为字符串)生成相同的XML:
<mc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <fm xsi:type="ArrayOfString"> <string>list</string> <string>entry</string> </fm> </mc>
由于序列化程序对"ArrayOfString"
和string[]
使用相同的xsi:type
多态名称List<string>
,当它找到可能遇到两者的情况时,它会抛出一个例外,因为它无法区分它们。
为什么XmlSerializer
同时使用相同的名称?我只能猜测它能够交换通过序列化不同集合类型(例如从List<TElement>
到SortedSet<TElement>
)创建的XML,而无需修复XML文件格式。
但是,在您的情况下,您需要在XML中区分这些类型的集合,而不是交换它们。因此,您将需要创建某种包装类,以允许在文件中区分它们。
例如,请考虑以下类的简化:
[XmlInclude(typeof(string))]
[XmlInclude(typeof(string[]))]
[XmlInclude(typeof(object[]))]
[XmlInclude(typeof(List<string>))]
[XmlInclude(typeof(List<object>))]
[XmlInclude(typeof(SortedSet<string>))]
[XmlInclude(typeof(SortedSet<object>))]
[XmlInclude(typeof(HashSet<string>))]
[XmlInclude(typeof(HashSet<object>))]
[XmlInclude(typeof(LinkedList<string>))]
[XmlInclude(typeof(LinkedList<object>))]
[XmlRoot(ElementName = "mc")]
public class MyClass
{
[XmlElement("fm")]
public object FirstMember;
}
这里FirstMember
可以包含字符串,字符串或对象的数组,或各种类型的字符串或对象集合。
要为各种类型的集合建立不同的xsi:type
值,可以引入以下通用包装类型:
/// <summary>
/// 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.
/// </summary>
public abstract class CollectionWrapper
{
[XmlIgnore]
public abstract IEnumerable RealCollection { get; }
static bool TryCreateWrapperType<TElement>(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>(TElement item)
{
if (item == null)
return item;
var type = item.GetType();
if (type == typeof(TElement))
return item;
Type wrapperType;
if (!TryCreateWrapperType<TElement>(type, out wrapperType))
return item;
return (TElement)Activator.CreateInstance(wrapperType, item);
}
public static TElement Unwrap<TElement>(TElement item)
{
if (item is CollectionWrapper)
return (TElement)((CollectionWrapper)(object)item).RealCollection;
return item;
}
}
/// <summary>
/// Generic wrapper type for a generic collection of items.
/// </summary>
/// <typeparam name="TCollection"></typeparam>
/// <typeparam name="TElement"></typeparam>
public class CollectionWrapper<TCollection, TElement> : CollectionWrapper where TCollection : ICollection<TElement>, new()
{
public class CollectionWrapperEnumerable : IEnumerable<TElement>
{
readonly TCollection collection;
public CollectionWrapperEnumerable(TCollection collection)
{
this.collection = collection;
}
public void Add(TElement item)
{
collection.Add(CollectionWrapper.Unwrap<TElement>(item));
}
#region IEnumerable<TElement> Members
public IEnumerator<TElement> 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<TElement> : CollectionWrapper<List<TElement>, TElement>
{
public ListWrapper() : base() { }
public ListWrapper(List<TElement> list) : base(list) { }
}
public class HashSetWrapper<TElement> : CollectionWrapper<HashSet<TElement>, TElement>
{
public HashSetWrapper() : base() { }
public HashSetWrapper(HashSet<TElement> list) : base(list) { }
}
public class SortedSetWrapper<TElement> : CollectionWrapper<SortedSet<TElement>, TElement>
{
public SortedSetWrapper() : base() { }
public SortedSetWrapper(SortedSet<TElement> list) : base(list) { }
}
public static class TypeExtensions
{
/// <summary>
/// Return all interfaces implemented by the incoming type as well as the type itself if it is an interface.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static IEnumerable<Type> 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<Type> 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<string>))]
[XmlInclude(typeof(ListWrapper<object>))]
[XmlInclude(typeof(SortedSetWrapper<string>))]
[XmlInclude(typeof(SortedSetWrapper<object>))]
[XmlInclude(typeof(HashSetWrapper<string>))]
[XmlInclude(typeof(HashSetWrapper<object>))]
[XmlInclude(typeof(CollectionWrapper<LinkedList<string>, string>))]
[XmlInclude(typeof(CollectionWrapper<LinkedList<object>, 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<string> { "list", "entry" } };
生成以下XML:
<mc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <fm xsi:type="ListWrapperOfString"> <Item>list</Item> <Item>entry</Item> </fm> </mc>
如您所见,xsi:type
现在是截然不同的。
如果我创建以下更复杂的对象:
var myClass = new MyClass
{
FirstMember = new List<object>
{
new List<object> { new List<object> { new List<object> { "hello" } }, "there" },
new HashSet<string> { "hello", "hello", "there" },
new SortedSet<string> { "hello", "hello", "there" },
new LinkedList<object>( new object [] { new LinkedList<string>( new [] { "hello", "there" }) }),
}
};
生成以下XML:
<mc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <fm xsi:type="ListWrapperOfObject"> <Item xsi:type="ListWrapperOfObject"> <Item xsi:type="ListWrapperOfObject"> <Item xsi:type="ListWrapperOfObject"> <Item xsi:type="xsd:string">hello</Item> </Item> </Item> <Item xsi:type="xsd:string">there</Item> </Item> <Item xsi:type="HashSetWrapperOfString"> <Item>hello</Item> <Item>there</Item> </Item> <Item xsi:type="SortedSetWrapperOfString"> <Item>hello</Item> <Item>there</Item> </Item> <Item xsi:type="CollectionWrapperOfLinkedListOfObjectObject"> <Item xsi:type="CollectionWrapperOfLinkedListOfStringString"> <Item>hello</Item> <Item>there</Item> </Item> </Item> </fm> </mc>
每个不同的集合类型如何有自己独特的xsi:type
。