我正在使用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();
}
}
当我这样做时,我得到ArgumentException
“流不可读”。
我可能在这里做错了所以所有答案都欢迎。感谢。
答案 0 :(得分:107)
三件事:
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();
}
}
但还有另一种更简单的选择。阅读时您正在使用流进行的所有操作都将其转换为字符串。你可以更简单地做到这一点:
return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int) stream.Length);
不幸的是,如果流已关闭,MemoryStream.Length
将抛出,因此您可能想要调用不关闭基础流的StreamWriter
构造函数,或者只是不要关闭{ {1}}。
我很担心你直接写到流 - 什么是StreamWriter
?它是XML序列化程序还是二进制序列化程序?如果它是二进制的,那么你的模型有些缺陷 - 你不应该混淆二进制和文本数据而不要非常小心。如果它是XML,您可能会发现在字符串中间最终会出现字节顺序标记,这可能会有问题。
答案 1 :(得分:7)
将内存流位置设置为开头可能会有所帮助。
stream.Position = 0;
但核心问题是StreamWriter在关闭时会关闭你的内存流。
只需刷新那个结束使用块的流,然后只处理它 fter 你已经从内存流中读取数据将为你解决这个问题。
您可能还想考虑使用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();
}
这将要求您的序列化WriteObject调用可以接受TextWriter而不是Stream。
答案 2 :(得分:3)
要在关闭后访问MemoryStream 的内容,请使用ToArray()
或GetBuffer()
方法。以下代码演示了如何将内存缓冲区的内容作为UTF8编码的字符串。
byte[] buff = stream.ToArray();
return Encoding.UTF8.GetString(buff,0,buff.Length);
注意:ToArray()
比GetBuffer()
更易于使用,因为ToArray()
返回流的确切长度,而不是缓冲区大小(可能大于流内容)。 ToArray()
复制字节。
注意:GetBuffer()
比ToArray()
更高效,因为它不会复制字节。您需要考虑流长度而不是缓冲区大小,在缓冲区末尾处理可能未定义的尾随字节。如果流大小大于80000字节,强烈建议使用GetBuffer()
,因为ToArray副本将在大对象堆上分配,因为它的生命周期可能会成为问题。
也可以按如下方式克隆原始MemoryStream,以便通过StreamReader访问它,例如
using (MemoryStream readStream = new MemoryStream(stream.ToArray()))
{
...
}
理想的解决方案是在关闭之前访问原始的MemoryStream,如果可能的话。
答案 3 :(得分:1)
只是一个疯狂的猜测:也许你需要冲洗算术者?可能系统发现写入“待定”。通过刷新,您确定该流包含所有书写字符并且可读。