2009-08-05 40 views
44

Tôi đang sử dụng DataContractJsonSerializer, thích xuất sang Luồng. Tôi muốn đầu và đuôi kết quả đầu ra của serializer vì vậy tôi đã sử dụng một StreamWriter để luân phiên viết trong các bit thêm tôi cần thiết.Viết để sau đó đọc từ MemoryStream

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

Khi tôi làm điều này, tôi nhận được ArgumentException "Luồng không đọc được".

Tôi có thể làm tất cả các loại sai ở đây để tất cả các câu trả lời đều được chào đón. Cảm ơn.

Trả lời

86

Ba điều:

  • Đừng đóng StreamWriter. Điều đó sẽ đóng MemoryStream. Bạn cần phải tuôn ra các nhà văn mặc dù.
  • Đặt lại vị trí của luồng trước khi đọc.
  • Nếu bạn định ghi trực tiếp vào luồng, trước tiên bạn cần xóa văn bản.

Vì vậy:

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

Có một lựa chọn đơn giản hơn mặc dù. Tất cả những gì bạn đang làm với luồng khi đọc đang chuyển đổi nó thành chuỗi. Bạn có thể làm điều đó đơn giản hơn:

return Encoding.UTF8.GetString(stream.GetBuffer(), 0, (int) stream.Length); 

Thật không may MemoryStream.Length sẽ ném nếu suối đã bị đóng cửa, vì vậy bạn có thể muốn gọi StreamWriter xây dựng mà không đóng dòng cơ bản, hoặc chỉ làm không đóng số StreamWriter.

Tôi quan tâm đến việc bạn viết trực tiếp lên luồng - ser là gì? Nó là một serializer XML, hoặc một nhị phân? Nếu nó là nhị phân, mô hình của bạn có phần thiếu sót - bạn không nên trộn dữ liệu nhị phân và văn bản mà không phải rất cẩn thận về nó. Nếu đó là XML, bạn có thể thấy rằng bạn kết thúc với các dấu thứ tự byte ở giữa chuỗi của bạn, điều này có thể có vấn đề.

+0

Cảm ơn Jon. Đó là một DataContractJsonSerializer. Tôi đã chỉnh sửa mã ngay bây giờ để hiển thị điều đó. Điều đó có làm cho những gì tôi đang làm không? – Gaz

+1

không nghĩ đến việc lấy bộ đệm - đẹp nhất – ShuggyCoUk

+0

Gaz: Tôi nghi ngờ rằng sẽ ổn, mặc dù tôi không hoàn toàn chắc chắn. Bạn có thể muốn xem xét điều gì sẽ xảy ra nếu XML chứa một cú đúp đóng ... –

6

đặt vị trí của luồng bộ nhớ ở đầu có thể hữu ích.

stream.Position = 0; 

Nhưng vấn đề cốt lõi là StreamWriter đóng luồng bộ nhớ của bạn khi đóng.

Đơn giản chỉ cần xả luồng mà bạn kết thúc khối sử dụng cho nó và chỉ xử lý khối đó fter bạn đã đọc dữ liệu trong luồng bộ nhớ sẽ giải quyết vấn đề này cho bạn.

Bạn cũng có thể muốn xem xét việc sử dụng một StringWriter thay vì ...

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

này sẽ yêu cầu cuộc gọi writeObject serialization của bạn có thể chấp nhận một TextWriter thay vì một Stream.

+0

Rất tiếc, tôi đã thêm tuyên bố dịch vụ ngay bây giờ. Nó chỉ chấp nhận một XmlWriter, mà không thực sự giúp tôi như tôi không thể sau đó chỉ cần thêm '{' s và bất cứ điều gì. Tôi đoán nó sử dụng XmlSerializer bên dưới nhưng tôi không muốn phải suy nghĩ về điều đó ngay bây giờ. Đặt vị trí không có hiệu lực. – Gaz

1

Chỉ là một dự đoán hoang dã: có thể bạn cần xóa luồng viết? Có thể hệ thống thấy rằng có ghi "đang chờ xử lý". Bằng cách đỏ mặt bạn biết chắc chắn rằng luồng chứa tất cả các ký tự được viết và có thể đọc được.

3

Để truy cập nội dung của MemoryStream sau khi đã đóng cửa sử dụng phương thức ToArray() hoặc GetBuffer(). Đoạn mã sau đây minh họa cách lấy nội dung của bộ nhớ đệm dưới dạng chuỗi được mã hóa UTF8.

byte[] buff = stream.ToArray(); 
return Encoding.UTF8.GetString(buff,0,buff.Length); 

Lưu ý: ToArray() là đơn giản để sử dụng hơn GetBuffer()ToArray() trả về chiều dài chính xác của các dòng, chứ không phải là kích thước bộ đệm (có thể lớn hơn so với nội dung stream). ToArray() tạo một bản sao của các byte.

Lưu ý: GetBuffer() có hiệu suất cao hơn ToArray(), vì nó không tạo bản sao của các byte. Bạn cần phải quan tâm đến các byte có thể không xác định được ở cuối bộ đệm bằng cách xem xét độ dài luồng thay vì kích thước bộ đệm. Sử dụng GetBuffer() được khuyến cáo mạnh mẽ nếu kích thước luồng lớn hơn 80000 byte vì bản sao ToArray sẽ được cấp phát trên Heap đối tượng lớn nơi thời gian tồn tại của nó có thể trở thành vấn đề.

Cũng có thể sao chép MemoryStream gốc như sau, để tạo điều kiện truy cập nó qua StreamReader, ví dụ:

using (MemoryStream readStream = new MemoryStream(stream.ToArray())) 
{ 
... 
} 

Giải pháp lý tưởng là truy cập MemoryStream gốc trước khi đóng cửa, nếu có thể.

+1

"Lưu ý: ToArray() tốt hơn GetBuffer() vì ToArray() trả về độ dài chính xác của luồng, thay vì kích thước bộ đệm (có thể lớn hơn nội dung luồng)." Nhưng tại sao bạn quan tâm? Không có nhược điểm khi truyền một mảng quá cỡ tới 'Encoding.GetString' - chúng ta biết chính xác số byte để cho nó giải mã. Thực tế là 'GetBuffer' * không * tạo một bản sao chính xác là lý do tại sao bạn sử dụng nó - nó tránh tạo bản sao vô nghĩa mà' ToArray' tạo ra. –

+0

Điểm tốt @JonSkeet Tôi sẽ cập nhật câu trả lời của mình để phản ánh những cân nhắc về hiệu suất. Nếu có ai quan tâm, có một câu hỏi trao đổi ngăn xếp về giá trị của MemoryStream.GetBuffer tại đây https://stackoverflow.com/questions/13053739/when-is-getbuffer-on-memorystream-ever-useful –

Các vấn đề liên quan