我正在使用DataContractJsonSerializer,它喜欢输出到Stream.我想要对串行器的输出进行顶部和尾部处理,因此我使用StreamWriter交替写入我需要的额外位.
var ser = new DataContractJsonSerializer(typeof (TValue));
using (var stream = new MemoryStream())
{
using (var sw = new StreamWriter(stream))
{
sw.Write("{");
foreach (var kvp in keysAndValues)
{
sw.Write("'{0}':", kvp.Key);
ser.WriteObject(stream, kvp.Value);
}
sw.Write("}");
}
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd();
}
}
Run Code Online (Sandbox Code Playgroud)
当我这样做时,我得到一个ArgumentException"流不可读".
我可能在这里做错了所以所有答案都欢迎.谢谢.
Jon*_*eet 114
三件事:
StreamWriter.这将关闭MemoryStream.你确实需要刷新编写器.所以:
using (var stream = new MemoryStream())
{
var sw = new StreamWriter(stream);
sw.Write("{");
foreach (var kvp in keysAndValues)
{
sw.Write("'{0}':", kvp.Key);
sw.Flush();
ser.WriteObject(stream, kvp.Value);
}
sw.Write("}");
sw.Flush();
stream.Position = 0;
using (var streamReader = new StreamReader(stream))
{
return streamReader.ReadToEnd();
}
}
Run Code Online (Sandbox Code Playgroud)
还有另一个更简单的选择.阅读时您正在使用流进行的所有操作都将其转换为字符串.你可以更简单地做到这一点:
return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int) stream.Length);
Run Code Online (Sandbox Code Playgroud)
不幸的是,MemoryStream.Length如果流已经关闭,则抛出,因此您可能想要调用StreamWriter不关闭底层流的构造函数,或者只是不要关闭StreamWriter.
我很担心你直接写到流 - 什么是ser?它是XML序列化程序还是二进制序列化程序?如果它是二进制的,那么你的模型有些缺陷 - 你不应该混淆二进制和文本数据而不要非常小心.如果它是XML,您可能会发现在字符串中间最终会出现字节顺序标记,这可能会有问题.
将内存流位置设置为开头可能会有所帮助.
stream.Position = 0;
Run Code Online (Sandbox Code Playgroud)
但核心问题是StreamWriter在关闭时关闭了你的内存流.
简单地冲洗你在哪里结束的使用块,并仅处置它是流压脚提升您已经阅读了数据从内存流将解决这个给你的.
您可能还想考虑使用StringWriter而不是......
using (var writer = new StringWriter())
{
using (var sw = new StreamWriter(stream))
{
sw.Write("{");
foreach (var kvp in keysAndValues)
{
sw.Write("'{0}':", kvp.Key);
ser.WriteObject(writer, kvp.Value);
}
sw.Write("}");
}
return writer.ToString();
}
Run Code Online (Sandbox Code Playgroud)
这将要求您的序列化WriteObject调用可以接受TextWriter而不是Stream.
要在关闭后访问 MemoryStream 的内容,请使用ToArray()或GetBuffer()方法。以下代码演示了如何以 UTF8 编码字符串的形式获取内存缓冲区的内容。
byte[] buff = stream.ToArray();
return Encoding.UTF8.GetString(buff,0,buff.Length);
Run Code Online (Sandbox Code Playgroud)
注意:ToArray()比GetBuffer()因为ToArray()返回流的确切长度而不是缓冲区大小(可能大于流内容)而使用更简单。 ToArray()复制字节。
注意:GetBuffer()比 性能更高ToArray(),因为它不会复制字节。您确实需要通过考虑流长度而不是缓冲区大小来处理缓冲区末尾可能存在的未定义尾随字节。GetBuffer()如果流大小大于 80000 字节,强烈建议使用,因为 ToArray 副本将分配在大对象堆上,在那里它的生命周期可能会出现问题。
也可以按如下方式克隆原始 MemoryStream,以方便通过 StreamReader 访问它,例如
using (MemoryStream readStream = new MemoryStream(stream.ToArray()))
{
...
}
Run Code Online (Sandbox Code Playgroud)
如果可能,理想的解决方案是在关闭之前访问原始 MemoryStream。