InvalidOperationException:参数对象’Scratch’的类型不是原始的

所以有错误。

InvalidOperationException:参数对象’Scratch’的类型不是原始的

我正在做的是序列化类ListList )。 BaseEnemy类还有一个类ListList )。 当我运行时,BaseEnemy列表正确序列化。 但是,BaseMoves列表没有。 Scratch是一个派生自BaseMove的类,它存储在List

这就是我遇到的问题。 我在这里找到了答案…… 这里 ……

“使用XmlInclude属性标记BaseMove类,将派生类作为参数传递:”

 [XmlInclude(typeof(Scratch))] public class BaseMove { public BaseMove() { } } 

奇迹般有效! 那我为什么要在这里发帖呢? 新问题。 我有大量来自BaseMove的动作。 有没有快捷方式,还是我必须在XmlInclude中编写每一个“typeof(…)”?

编辑 – 我找到的另一种解决方案。

 public static Type[] GetAllSubTypes(Type aBaseClass) { List result = new List(); Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly a in assemblies) { Type[] types = a.GetTypes(); foreach (Type t in types) { if (t.IsSubclassOf(aBaseClass)) result.Add(t); } } return result.ToArray(); } 

然后在序列化过程中,我只是将此函数称为extraTypes数组。

 Type[] extraTypes = GetAllSubTypes(typeof(BaseMove)); XmlSerializer xml = new XmlSerializer(typeof(List), extraTypes); 

您可以遍历应用程序域中的所有程序集,查找可从基本类型分配的所有类型,并在构造XmlSerializer时将它们作为已知类型传递。 但是,一些警告:

  1. 程序集按需加载,因此您只能执行一次搜索,并且
  2. XmlSerializer一旦构造,必须缓存在哈希表或字典中并重用以防止内存和资源泄漏。 详情请见此处 。

因此,您可以执行以下操作:

 public static class XmlSerializerWithKnownTypeCreator { static Dictionary, XmlSerializer> table = new Dictionary, XmlSerializer>(HashSet.CreateSetComparer()); public static XmlSerializer CreateKnownTypeSerializer() { return CreateKnownTypeSerializer(new Type [] {typeof(TRoot)}); } public static XmlSerializer CreateKnownTypeSerializer(IEnumerable baseTypes) { var set = new HashSet( AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => baseTypes.Any(baseType => baseType.IsAssignableFrom(t)))); lock (table) { XmlSerializer serializer; if (table.TryGetValue(set, out serializer)) return serializer; table[set] = serializer = new XmlSerializer(typeof(T), set.ToArray()); return serializer; } } } 

称之为:

 var serializer = XmlSerializerWithKnownTypeCreator.CreateKnownTypeSerializer(); 

例如,

 var serializer = XmlSerializerWithKnownTypeCreator>.CreateKnownTypeSerializer(); 

更新

如果序列化程序的参数化通用静态表看起来很奇怪(并且公平地说,它确实如此),您可以将序列化程序存储在非通用的全局哈希表中,如文档中所示 :

如果使用任何其他构造函数,则会生成同一程序集的多个版本,并且永远不会卸载,这会导致内存泄漏和性能下降。 最简单的解决方案是使用前面提到的两个构造函数之一。 否则,必须将程序集缓存在Hashtable中,如以下示例所示。

但是,doc如何为XmlSerializer生成密钥。 代码示例也不是线程安全的,也许因为所有这些东西都可以追溯到.Net 1.0。 所以这里有一些逻辑可以在全局哈希表中正确地键入和回收具有已知额外类型的XmlSerializer

 public abstract class XmlserializerKey { readonly Type serializerType; public XmlserializerKey(Type serializerType) { this.serializerType = serializerType; } protected Type SerializerType { get { return serializerType; } } public override bool Equals(object obj) { if (ReferenceEquals(this, obj)) return true; else if (ReferenceEquals(null, obj)) return false; if (GetType() != obj.GetType()) return false; XmlserializerKey other = (XmlserializerKey)obj; if (other.serializerType != serializerType) return false; return true; } public override int GetHashCode() { int code = 0; if (serializerType != null) code ^= serializerType.GetHashCode(); return code; } public override string ToString() { return string.Format("Serializer type: " + serializerType.ToString()); } } public abstract class XmlserializerKeyWithExtraTypes : XmlserializerKey { static IEqualityComparer> comparer; readonly HashSet moreTypes = new HashSet(); static XmlserializerKeyWithExtraTypes() { comparer = HashSet.CreateSetComparer(); } public XmlserializerKeyWithExtraTypes(Type serializerType, IEnumerable extraTypes) : base(serializerType) { if (extraTypes != null) foreach (var type in extraTypes) moreTypes.Add(type); } protected Type[] MoreTypes { get { return moreTypes.ToArray(); } } public override bool Equals(object obj) { if (!base.Equals(obj)) return false; XmlserializerKeyWithExtraTypes other = (XmlserializerKeyWithExtraTypes)obj; return comparer.Equals(moreTypes, other.moreTypes); } public override int GetHashCode() { int code = base.GetHashCode(); if (moreTypes != null) code ^= comparer.GetHashCode(moreTypes); return code; } } public sealed class XmlSerializerKeyWithKnownTypes : XmlserializerKeyWithExtraTypes { public XmlSerializerKeyWithKnownTypes(Type serializerType, IEnumerable otherTypes) : base(serializerType, otherTypes) { } public XmlSerializer CreateSerializer() { return new XmlSerializer(SerializerType, MoreTypes); } } public static class XmlSerializerHashTable { static Dictionary dict; static XmlSerializerHashTable() { dict = new Dictionary(); } public static XmlSerializer DemandSerializer(object key, Func factory) { lock (dict) { XmlSerializer value; if (!dict.TryGetValue(key, out value)) dict[key] = value = factory(key); return value; } } } public static class XmlSerializerWithKnownDerivedTypesCreator { public static XmlSerializer CreateKnownTypeSerializer(Type type, IEnumerable extraTypes) { var allExtraTypes = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(a => a.GetTypes()) .Where(t => extraTypes.Any(extraType => extraType.IsAssignableFrom(t))); var key = new XmlSerializerKeyWithKnownTypes(type, allExtraTypes); return XmlSerializerHashTable.DemandSerializer(key, k => ((XmlSerializerKeyWithKnownTypes)k).CreateSerializer()); } } 

然后以与调用等效的XmlSerializer构造函数相同的方式调用它:

  public static void Test2() { List list = new List(); list.Add(new BaseClass()); list.Add(new MidClass()); list.Add(new DerivedClass1()); list.Add(new DerivedClass2()); var serializer = XmlSerializerWithKnownDerivedTypesCreator.CreateKnownTypeSerializer(list.GetType(), new Type[] { typeof(BaseClass) }); string xml = XmlSerializationHelper.GetXml(list, serializer, false); Debug.WriteLine(xml); // No assert below: Debug.Assert(object.ReferenceEquals(serializer, XmlSerializerWithKnownDerivedTypesCreator.CreateKnownTypeSerializer(list.GetType(), new Type[] { typeof(BaseClass) }))); }