反序列化对象时没有对象错误的映射

时间:2012-12-02 22:04:11

标签: c# object serialization encoding stream

我有以下C#代码应该将任意对象序列化为字符串,然后当然反序列化它。

public static string Pack(Message _message)
{
   BinaryFormatter formatter = new BinaryFormatter();

   MemoryStream original = new MemoryStream();
   MemoryStream outputStream = new MemoryStream();

   formatter.Serialize(original, _message);
   original.Seek(0, SeekOrigin.Begin);

   DeflateStream deflateStream = new DeflateStream(outputStream, CompressionMode.Compress);
   original.CopyTo(deflateStream);

   byte[] bytearray = outputStream.ToArray();

   UTF8Encoding encoder = new UTF8Encoding();
   string packed = encoder.GetString(bytearray);
   return packed;
} 

public static Message Unpack(string _packed_message)
{
    UTF8Encoding encoder = new UTF8Encoding();
    byte[] bytearray = encoder.GetBytes(_packed_message);

    BinaryFormatter formatter = new BinaryFormatter();

    MemoryStream input = new MemoryStream(bytearray);
    MemoryStream decompressed = new MemoryStream();

    DeflateStream deflateStream = new DeflateStream(input, CompressionMode.Decompress);
    deflateStream.CopyTo(decompressed); // EXCEPTION

    decompressed.Seek(0, SeekOrigin.Begin);

    var message = (Message)formatter.Deserialize(decompressed); // EXCEPTION 2
    return message;
}

但问题是,无论何时运行代码,我都会遇到异常。使用上面的代码并调用它,如下所示,我收到 InvalidDataException:未知的块类型。流可能已损坏。标记为// EXCEPTION行。

在寻找这个问题后,我试图放弃通缩。这只是一个很小的变化:在Pack中,bytearray来自original.ToArray()UnpackSeek() input而不是{{} 1}}并使用decompressed代替Deserialize(input)。唯一改变的结果是:异常位置和身体是不同的,但它仍然发生。我在decompressed收到 SerializationException:没有对象'201326592'的地图。

我似乎没有看到问题所在。也许这是整个序列化的想法......问题是以某种方式管理打包 // EXCEPTION 2实例是必要的,因为这些对象包含在服务器和客户端应用程序之间传递的信息。 (序列化逻辑位于 .Shared DLL项目中,两端都有引用,但是,现在,我只是首先开发服务器端。)还必须告诉我,我我只使用Message输出,因为现在,服务器和客户端之间的TCP连接基于两端的字符串读写。所以它必须以某种方式降低到字符串的水平。

这就是Message对象的样子:

string

[Serializable] public class Message { public MessageType type; public Client from; public Client to; public string content; } 现在只是一个空类,只有Client属性,没有属性或方法。)

这就是如何调用pack-unpack(来自Serializable ...):

Main()

这是显示IDE中发生的情况的图像。控制台窗口中的输出值为 Shared.Message msg = Shared.MessageFactory.Build(Shared.MessageType.DEFAULT, new Shared.Client(), new Shared.Client(), "foobar"); string message1 = Shared.MessageFactory.Pack(msg); Console.WriteLine(message1); Shared.Message mess2 = Shared.MessageFactory.Unpack(message1); // Step into... here be exceptions Console.Write(mess2.content); Image showing the Exception happening and the output

遗憾的是,一些调查还发现问题可能在于message1变量。运行bytearray时,在编码器创建字符串后,数组包含152个值,但是,在Pack()中解码后,数组会改为 160

我很感激任何帮助,因为我真的没有想法,并且遇到这个问题,进展就会瘫痪。谢谢。

(更新)最终解决方案:

我要感谢所有回答和评论的人,因为我已经达成了解决方案。谢谢。

Marc Gravell是对的,我错过了Unpack()的结束,因此,结果是空的或已损坏。我花了很多时间重新思考并重写了这些方法,现在它完美无缺。甚至通过网络流发送这些字节的目的也是有效的。

此外,正如Eric J.建议的那样,当数据流入deflateStream时,我已切换到使用ASCIIEnconding进行stringbyte[]之间的更改

固定代码如下:

Stream

现在一切都恰到好处,正如下面的截图所示。这是第一名的预期产出。 服务器客户端可执行文件相互通信,消息传播......并且它被正确序列化和反序列化。

Image showing proper output

3 个答案:

答案 0 :(得分:5)

除了有关Encoding vs base-64的现有观察之外,请注意您尚未关闭deflate流。这很重要,因为压缩流缓冲:如果你不关闭,它可能不会写结束。对于短流,这可能意味着它什么都不写。

using(DeflateStream deflateStream = new DeflateStream(
    outputStream, CompressionMode.Compress))
{
    original.CopyTo(deflateStream);
}
return Convert.ToBase64String(outputStream.GetBuffer(), 0,
    (int)outputStream.Length);

答案 1 :(得分:2)

您的问题很可能是UTF8编码。您的字节实际上不是字符串,UTF-8是字符长度不同的编码。 这意味着字节数组可能与正确编码的UTF-8字符串不对应(例如,最后可能会丢失一些字节。)

尝试使用UTF16或ASCII作为恒定长度编码(结果字符串可能包含控制字符,因此无法通过HTTP或电子邮件等方式打印或传输。)

但是如果你想编码为字符串,通常使用UUEncoding将字节数组转换为真正的可打印字符串,那么你可以使用你想要的任何编码。

答案 2 :(得分:2)

当我对Pack()和Unpack()运行以下Main()代码时:

    static void Main(string[] args)
    {
        Message msg = new Message() { content = "The quick brown fox" };

        string message1 = Pack(msg);
        Console.WriteLine(message1);

        Message mess2 = Unpack(message1); // Step into... here be exceptions
        Console.Write(mess2.content);
    }

我看到了bytearray

byte[] bytearray = outputStream.ToArray();

是空的。

我确实稍微修改了您的序列化类,因为您没有为包含的类发布代码

public enum MessageType
{
    DEFAULT = 0
}

[Serializable]
public class Message
{
    public MessageType type;
    public string from;
    public string to;
    public string content;
}

我建议采取以下步骤解决此问题:

  • 沿途检查中间结果。你还看到数组中有0个字节吗? Pack()返回的字符串值是什么?
  • 完成后,请处理您的溪流。最简单的方法是使用using关键字。

修改

正如Eli和Marc正确指出的那样,你不能在UTF8字符串中存储任意字节。映射不是双射的(你不能在没有信息丢失/失真的情况下来回转换)。您将需要一个双射的映射,例如Marc建议的Convert.ToBase64String()方法。