0

我正在编写一个 XML 序列化程序,它使用反射递归地爬过对象的公共或私有字段以存储为 XML 并稍后重建,并且在臭名昭著的不可序列化 DataTable 上测试我的序列化程序时,它以某种方式序列化然后尝试重新实例化某种指针,并且在这样做时越过了托管/非托管边界并且(谢天谢地)崩溃而不是乱码内存。我需要找到解决这个问题的方法,但我有点迷茫,因为我没有非托管代码的背景。

我知道您实际上不能按原样序列化指针或引用,因为指针或引用的值是内存地址,并且在重新实例化指针时,您不能期望正确的对象位于该地址XML。据我了解,我要么需要检测一个会导致此问题的对象并忽略它,要么找到并序列化指向的对象,然后在反序列化时对该对象进行反序列化,然后将指针指向所述对象的位置。但我也不知道该怎么做;我的第一个猜测是过滤 Type.IsPointer 似乎并没有解决问题。我要求的可以完成吗?有更好的解决方案吗?我可以用一些战略性的非托管代码来做到这一点吗?

上下文:我正在制作一个序列化程序,它可以序列化普通 XmlSerializer 不能序列化的类型(IDictionary、具有循环引用的类型等)。我的序列化器忽略了属性和 ISerializeable 或 IXMLSerializeable 的实现;它盲目地使用一些规则递归地序列化对象的所有字段。它有效,但它与某些对象猛烈撞击本机/托管边界。我没有使用二进制序列化,因为我的对象不断被修改,而且我不知道如何解决与二进制序列化的对象版本冲突。

编辑:这是在尝试重新实例化“System.Globalization.TextInfo”类时崩溃的代码,我认为这是深埋在 DataTable 某处的文化对象的一部分。这些函数递归地相互调用(总是从 ReInstantiateValueInstance 开始),直到重新实例化初始类型参数。

重新实例化 System.Globalization.TextInfo(CultureInfo) 时,在“bestCtor.Invoke(parameters.ToArray())”处引发托管/本机边界异常

    protected object ReCreateTypeWithParameters(Type t)
    {
        if (t.ToString() == "System.Type") return typeof(object); //we dont know the type of type

        var construct = StoreUtilities.GetConstructors(t); //gets any and all constructors for an object

        if (construct != null && construct.Count > 0)
        {
            var leastParams = (from c in construct
                               select c.GetParameters().Count()).Min();

            var bestCtor = (from c in construct
                            where c.GetParameters().Count() == leastParams
                            select c).FirstOrDefault(); //the best constructor has the least parameters - less can go wrong

            if (bestCtor != null)
            {
                List<object> parameters = new List<object>();

                foreach (var param in bestCtor.GetParameters())
                {
                    parameters.Add(ReInstantiateValueInstance(param.ParameterType));
                }

                return bestCtor.Invoke(parameters.ToArray()); //pointer types go boom here.
            }
        }           

        return null;
    }

    protected virtual object ReInstantiateValueInstance(Type t)
    {
        try
        {       
            if (t.ToString() == "System.Type") //we don't know the Type of Type
            {
                return typeof(object);
            }
            var construct = StoreUtilities.GetConstructors(t, true); //gets an object's parameterless constructors

            if (construct == null && t.IsGenericType) //no constructor, it's generic
            {
                object generic = ReCreateGenericType(t);

                if (generic == null) //if the generic type had no constructor, we use the activator.
                {
                    return Activator.CreateInstance(t);
                }
                else
                {
                    return generic;
                }
            }

            if (construct == null || construct.Count() == 0) //we have no constuctor. Try and make a placeholder object anyways.
            {
               return ReCreateTypeWithParameters(t);
            }

            object o = construct.First().Invoke(null);
            return o;
        }
        catch
        {
            return null;
        }
    }

    protected object ReCreateGenericType(Type t)
    {
        try
        {
            if (Type.IsGenericType != true) return null;
            var construct = StoreUtilities.GetConstructors(Type, false);

            if (construct != null && construct.Count() > 0)
            {
                construct = construct.OrderBy(i => i.GetParameters().Count()).ToList();
                var tParams = construct[0].GetParameters();
                List<object> paramList = new List<object>();

                foreach (var p in tParams)
                {
                    if (StoreUtilities.CanStoreAsString(p.ParameterType) == true)
                    {
                        object o = Activator.CreateInstance(p.ParameterType);
                        paramList.Add(o);
                    }
                    else
                    {
                        paramList.Add(ReInstantiateValueInstance(p.ParameterType));
                    }
                }

                return construct[0].Invoke(paramList.ToArray());
            }
            else
            {
                return Activator.CreateInstance(t);
            }
        }
        catch
        {
            return null;
        }
    }
4

2 回答 2

0

您无法按照说明解决此问题。您只是不知道对象是否包含对“非托管数据”的引用。它可能将指向非托管内存的指针存储在Int64, String,byte[]等中。开发人员使用各种技巧。

如果你能以某种方式检测到这一点,然后“忽略”具有非托管引用的对象,那么你就输了,因为当你去反序列化你的数据时,你最终会得到一个不完整的对象。

解决此问题的唯一方法是借助您正在序列化的对象。他们可以通过可选接口实现帮助序列化/反序列化无法通过反射或属性找到的数据。

唯一可以在没有这种帮助的情况下普遍工作的序列化程序往往要求他们正在序列化的对象是 POCO,这是有原因的......

于 2013-03-07T04:03:44.050 回答
0

我不确定托管/非托管与此有何相关性,但做到这一点的基本方法是在内部引用的序列化数据中具有某种抽象,一个标签。在序列化期间,您将添加对字典或类似的引用以及标签,因此您只需序列化对象一次。

反序列化将反映这个过程,只创建一次具有特定标签的引用,其余引用通过标签查找现有实例。

于 2013-03-07T01:40:02.497 回答