我有以下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()
和Unpack
,Seek()
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);
。
遗憾的是,一些调查还发现问题可能在于message1
变量。运行bytearray
时,在编码器创建字符串后,数组包含152个值,但是,在Pack()
中解码后,数组会改为 160 。
我很感激任何帮助,因为我真的没有想法,并且遇到这个问题,进展就会瘫痪。谢谢。
我要感谢所有回答和评论的人,因为我已经达成了解决方案。谢谢。
Marc Gravell是对的,我错过了Unpack()
的结束,因此,结果是空的或已损坏。我花了很多时间重新思考并重写了这些方法,现在它完美无缺。甚至通过网络流发送这些字节的目的也是有效的。
此外,正如Eric J.建议的那样,当数据流入deflateStream
时,我已切换到使用ASCIIEnconding
进行string
和byte[]
之间的更改
固定代码如下:
Stream
现在一切都恰到好处,正如下面的截图所示。这是第一名的预期产出。 服务器和客户端可执行文件相互通信,消息传播......并且它被正确序列化和反序列化。
答案 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;
}
我建议采取以下步骤解决此问题:
using
关键字。修改强>
正如Eli和Marc正确指出的那样,你不能在UTF8字符串中存储任意字节。映射不是双射的(你不能在没有信息丢失/失真的情况下来回转换)。您将需要一个双射的映射,例如Marc建议的Convert.ToBase64String()方法。