在反序列化时缺少引用时不抛出异常

时间:2013-10-18 09:21:32

标签: c# .net deserialization

我正在为我正在使用的系统集合编写一个不可知的查看器。此查看器将向我显示我的数据的通用结构,而无需了解特定系统的上下文。

我正在尝试对仅包含Foo<T>类型Foo<T>的内存流进行反序列化,其中Foo继承自<T>。从不可知论的观点来看,我需要的所有数据都在Foo中。 [Serializable] public class Foo { public string Agnostic { get; set; } } [Serializable] public class Foo<T> : Foo { public string Contextual { get; set; } } 部分无关紧要。

类型T在另一个程序集中定义。在正常操作下,系统显然已加载所有适当的上下文程序集。问题是在运行查看器时,没有加载任何上下文程序集。当我试图反序列化Foo的实例时,我显然得到一个异常,因为没有加载引用的程序集。

我正在尝试检测是否已加载所有必需的引用程序集,因此知道是否尝试反序列化数据,或者从类的其他方面重构我需要的数据。

我知道我可以使用一个非常简单的异常try / catch块来做到这一点,但是,这不是一个例外情况。我知道当我加载数据时,这将发生数百次,甚至数千次,这可能会让我成为一场噩梦,因为我希望在打开异常时休息一下。我还订阅了一个思想学校,上面写着“异常 - 提示在名称中”,因此异常不应构成主要案例代码的一部分。

-------- edit 21/10/2013 ------------

请参阅here以获取完整的说明性示例,但以下是重要的内容:

Foo类,共同定义:

BinaryFormatter bf = new BinaryFormatter();
FileInfo tempFile = TempFileGetter.GetTempFile();


Foo<Bar> fooBar = new Foo<Bar>();
fooBar.Agnostic = "Agnostic";
fooBar.Contextual = "Contextual";


using (var fs = tempFile.OpenWrite())
{
   bf.Serialize(fs, fooBar);
   fs.Flush();
}

上下文保存:

BinaryFormatter bf = new BinaryFormatter();
FileInfo tempFile = TempFileGetter.GetTempFile();

using (var fs = tempFile.OpenRead())
{
   Foo foo = (Foo)bf.Deserialize(fs);
   Controls.DataContext = foo;
}

不可知载荷:

{{1}}

我的意思是,在这段代码中没有什么火箭科学,并且,如果“不可知”的查看器加载上下文查看器作为参考,那么它加载正常,但是,我不想这样做,因为我们赢了总是要加载上下文库。

2 个答案:

答案 0 :(得分:0)

我所做的是创建一个序列化容器,分析它的内容以查看需要哪些引用并单独序列化:

[serializable]
public class Container
{
    private IEnumerable<object> data;
    public Container(IEnumerable data);

    public string[] GetFullyQualifiedReferences();        
}

因此,您可以在此处输入您喜欢的任何数据,并获得一个完全限定的程序集名称列表,然后单独存储。

假设Wibble是序列化Foo的类

public class Wibble : ISerializable
{        

    public string Agnostic { get { return agnostic; } }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        //construct a container based on my data.
        Container container = new Container(new object[]{myFoo});
        info.AddValue("RequiredReferences",container.GetFullyQualifiedReferences());

        byte[] containerData = Serialize(container);
        info.AddValue("TypeUnsafeData",containerData);

        //store some safe typed versions of the context data, if necessary.
        info.AddValue("SafeValues", GetSafeValues())
    }

    public Wibble(SerializationInfo info, StreamingContext context)
    { 
        var requiredAssemblies = 
           (string[])info.GetValue("RequiredReferences",typeof(string[]));

        if(AreAssembliesLoaded(requiredAssemblies )))
        {
           //deserialise the container as normal
        }
        else
        {
           //instead, load the "safe" data that we previously stored.
        }

    }

}

我知道这个插图并没有完全合理,但插图是我实现问题的略微不完美的抽象,但解决方案应该适用于两者(我知道它适用于我的实现!)

您可以进行下一步,在容器中放置一个容器,列出任何容器的特定必需程序集,但在此图中并不是必需的。

------ -------更新

此实现存在一个小问题,即如果更新上下文程序集版本号,则反序列化程序将找不到旧程序集。所以,你要么:

提供某种机制,允许deseralize代码查看它是否可以找到程序集的现代版本然后查询它,

--- ---或

更新完全限定名称的相等性,使其对版本控制不敏感。

答案 1 :(得分:-1)

BinaryFormatter包含此信息,尝试将序列化数据读入MemoryStream并将其转换为字符串

// There's probably a better way to do this:
new String(memoryStream.GetBuffer().Select<byte, char>(b => (char)b).ToArray());

对于Assembly.Name.MyObject<int>你最终会得到一些看起来很难解析的东西,但它应该是可行的:

[ÿÿÿÿBAssembly.Name,Version = 1.0.0.0,Culture = neutral,PublicKeyToken = nullgMyObject`1 [[System.Int32,mscorlib,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089]] n1n2str]

或者,如果您可以控制序列化,则首先使用文件中所需的信息序列化对象(例如,仅用于T的类型和程序集),然后是BinaryFormatter中的数据。 (如果你愿意,我可以扩展这个。)

要查看是否加载了任何对象,可以使用Type.GetType("Assembly.Name.Space.ClassName"),因为如果找不到类型,则返回null,但是可以使用其他方法列出问题"how to check if namespace, class, or method exists"