【问题标题】:not throwing an exception when missing reference on deserialize反序列化缺少引用时不抛出异常
【发布时间】:2013-10-27 01:59:11
【问题描述】:

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

我正在尝试反序列化仅包含 Foo<T> 类型的内存流,其中 Foo<T> 继承自 Foo。从不可知论者的角度来看,我需要的所有数据都在 Foo 中。 <T> 部分无关紧要。

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

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

我知道我可以使用一个非常简单的异常 try/catch 块来做到这一点,但是,这不是异常情况。我知道当我加载我的数据时,这将发生数百甚至数千次,这可能会让我做噩梦,因为我喜欢打开异常中断。我还赞同说“异常 - 提示就在名称中”的思想流派,因此异常不应构成您的主要案例代码的一部分。

--------编辑 21/10/2013------------

请参阅 here 以获得完整的说明性示例,但这里是重要的部分:

Foo类,共同定义:

[Serializable]
public class Foo
{
    public string Agnostic { get; set; }
}

[Serializable]
public class Foo<T> : Foo
{
    public string Contextual { get; set; }
}

上下文保存:

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;
}

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

【问题讨论】:

  • 你能提供你的反序列化代码示例吗?可以改一下序列化代码吗?
  • 添加代码来说明问题(完整的可下载版本)

标签: c# .net deserialization


【解决方案1】:

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

[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.
        }

    }

}

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

您可以进行下一步,在一个容器中添加一个容器,列出任何容器所需的特定程序集,但在本例中这并不是必需的。

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

这个实现有一个小问题,如果你更新上下文程序集版本号,反序列化器将找不到旧的程序集。所以,你要么需要:

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

---或者---

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

【讨论】:

    【解决方案2】:

    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&lt;int&gt;,你最终会得到一些看起来很难解析的东西,但它应该是可行的:

    [ÿÿÿÿ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" 列出的其他方法可以替代使用。

    【讨论】:

    • 我不确定我是否对它作为解决方案感到满意,它看起来非常像打破序列化引擎的封装......如果二进制序列化程序使用的格式发生变化,或者我改变了我的序列化引擎,一切都会好起来的,但原因并不明显......
    • 如果你改变你的序列化引擎,任何预先存在的保存数据都会发生爆炸......但你是对的,这确实破坏了封装。因此,请编写您自己的序列化引擎,在调用内置引擎完成大部分工作之前写入一些元数据(类/程序集名称)。反序列化可以读取该元数据,检查该类是否存在,然后在适当时将其传递给内置引擎。如果您认为您可能会更改该引擎,您的自定义类可以包含一个标志来说明您正在使用哪个引擎,以便将来读取您的数据。
    • 哦,这就是你所做的,但使用类结构而不是文件中的数据。
    • 好吧,我不是在检查原始数据流;)但是,是的,这是一个类似的想法。当我建议引擎更改时,我并不是在暗示支持多序列化引擎(那是完全不同的蠕虫!),只是在封装的上下文之外假设实现细节的危险。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多