指针/内存引用的序列化/反序列化

时间:2013-03-07 00:59:19

标签: c# xml pointers serialization unsafe

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

我知道您实际上无法按原样序列化指针或引用,因为指针或引用的值是内存地址,并且在重新实例化时您不能指望正确的对象位于该地址来自XML的指针。据我了解,我要么检测一个会导致这个问题并忽略它的对象,要么找到并序列化指向的对象,然后在反序列化时取消该对象的deserailize,然后将指针指向所述对象的位置。但我不知道该怎么做;我的第一个猜测是过滤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;
        }
    }

2 个答案:

答案 0 :(得分:0)

我不确定托管/非托管是否与此有任何关联,但实现此目的的基本方法是在内部引用的序列化数据中使用某种抽象(标签)。在序列化期间,您可以添加对字典或类似字符的引用以及标签,因此您只需将对象序列化一次。

反序列化将镜像此过程,仅创建具有特定标签的引用一次,其余引用按标签查找现有实例。

答案 1 :(得分:0)

如上所述,您无法解决此问题。如果对象包含对“非托管数据”的引用,则您无法知道。它可能是将指针存储在Int64Stringbyte[]中的非托管内存中。开发人员使用各种技巧。

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

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

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