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

时间:2014-12-10 00:34:22

标签: c# xml list unity3d xml-serialization

所以有错误。

  

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

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

WAS 我遇到的问题。我在这里找到答案...... HERE ......

  

&#34;使用XmlInclude属性标记BaseMove类传递派生类   作为参数:&#34;

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

像魅力一样!那我为什么要在这里发帖呢?新问题。我有来自BaseMove的 HUNDREDS 移动。有没有捷径,或者我必须写每一个&#34; typeof(...)&#34;在XmlInclude中?

编辑 - 我发现的另一种解决方案。

public static Type[] GetAllSubTypes(Type aBaseClass)
{
    List<Type> result = new List<Type>();
    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<BaseEnemy>), extraTypes);

1 个答案:

答案 0 :(得分:2)

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

  1. 按需加载程序集,因此您只能执行一次搜索,而且
  2. XmlSerializer一旦构造,必须缓存在哈希表或字典中并重用以防止内存和资源泄漏。有关详细信息,请参阅here
  3. 因此,您可以执行以下操作:

    public static class XmlSerializerWithKnownTypeCreator<T>
    {
        static Dictionary<HashSet<Type>, XmlSerializer> table = new Dictionary<HashSet<Type>, XmlSerializer>(HashSet<Type>.CreateSetComparer());
    
        public static XmlSerializer CreateKnownTypeSerializer<TRoot>()
        {
            return CreateKnownTypeSerializer(new Type [] {typeof(TRoot)});
        }
    
        public static XmlSerializer CreateKnownTypeSerializer(IEnumerable<Type> baseTypes)
        {
            var set = new HashSet<Type>(
                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<DocumentRoot>.CreateKnownTypeSerializer<BaseMove>();
    

    例如,

    var serializer = XmlSerializerWithKnownTypeCreator<List<BaseClass>>.CreateKnownTypeSerializer<BaseMove>();
    

    <强>更新

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

      

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

    然而,文档对于如何为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<HashSet<Type>> comparer;
    
        readonly HashSet<Type> moreTypes = new HashSet<Type>();
    
        static XmlserializerKeyWithExtraTypes()
        {
            comparer = HashSet<Type>.CreateSetComparer();
        }
    
        public XmlserializerKeyWithExtraTypes(Type serializerType, IEnumerable<Type> 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<Type> otherTypes)
            : base(serializerType, otherTypes)
        {
        }
    
        public XmlSerializer CreateSerializer()
        {
            return new XmlSerializer(SerializerType, MoreTypes);
        }
    }
    
    public static class XmlSerializerHashTable
    {
        static Dictionary<object, XmlSerializer> dict;
    
        static XmlSerializerHashTable()
        {
            dict = new Dictionary<object, XmlSerializer>();
        }
    
        public static XmlSerializer DemandSerializer(object key, Func<object, XmlSerializer> 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<Type> 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<BaseClass> list = new List<BaseClass>();
            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) })));
        }