.NET 6中如何从NetworkStream/SslStream正确反序列化JSON对象
解决JsonSerializer在网络流上反序列化挂起的问题
核心问题在于JsonSerializer.Deserialize默认会持续读取流直到返回EOF,但NetworkStream/SslStream在连接未关闭时不会触发EOF,导致调用挂起。要实现读取单个JSON对象后立即停止、不占用后续流数据的需求,可采用以下两种方案:
方案一:直接使用Utf8JsonReader手动控制读取边界
利用JsonSerializer底层依赖的Utf8JsonReader,可以精准控制读取范围,完成单个JSON对象的反序列化后立即停止。
using var jsonReader = new Utf8JsonReader(stream, new JsonReaderOptions { AllowTrailingCommas = false, CommentHandling = JsonCommentHandling.Disallow }); // 定位到JSON对象起始标记 if (!jsonReader.Read() || jsonReader.TokenType != JsonTokenType.StartObject) { throw new InvalidDataException("响应不是有效的JSON对象"); } // 反序列化时,Utf8JsonReader会自动读取到对应的EndObject标记后停止 var response = JsonSerializer.Deserialize<TResponse>(ref jsonReader);
该方案无需额外封装,Utf8JsonReader会自动处理字符串中的转义字符,不会误判嵌套的{},是最可靠的实现方式。
方案二:自定义包装流,检测JSON对象结束标记
如果不想直接操作Utf8JsonReader,可以封装一个自定义流,通过跟踪JSON对象的嵌套深度,在读取到最外层}时返回EOF,让JsonSerializer停止读取。
public class JsonBoundaryStream : Stream { private readonly Stream _innerStream; private int _objectDepth; private bool _isCompleted; private readonly Queue<byte> _buffer = new Queue<byte>(); public JsonBoundaryStream(Stream innerStream) { _innerStream = innerStream; } public override int Read(byte[] buffer, int offset, int count) { if (_isCompleted) return 0; int bytesRead = 0; while (bytesRead < count && !_isCompleted) { if (_buffer.Count == 0) { byte[] tempBuffer = new byte[1]; int read = _innerStream.Read(tempBuffer, 0, 1); if (read == 0) { _isCompleted = true; break; } _buffer.Enqueue(tempBuffer[0]); } byte b = _buffer.Dequeue(); buffer[offset + bytesRead] = b; bytesRead++; // 跟踪JSON对象嵌套深度 if (b == '{') _objectDepth++; else if (b == '}') { _objectDepth--; if (_objectDepth == 0) _isCompleted = true; } } return bytesRead; } // 实现Stream其他抽象方法,直接转发给底层流 public override bool CanRead => _innerStream.CanRead; public override bool CanSeek => false; public override bool CanWrite => false; public override long Length => throw new NotSupportedException(); public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } public override void Flush() => _innerStream.Flush(); public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); public override void SetLength(long value) => throw new NotSupportedException(); public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); }
使用时只需将原流包装后传入:
using var boundaryStream = new JsonBoundaryStream(stream); var response = JsonSerializer.Deserialize<TResponse>(boundaryStream);
注意:该方案仅适用于最外层为对象的JSON响应,若响应是数组,需将检测逻辑改为跟踪[和]的深度;同时要确保JSON中没有未转义的{}出现在字符串中,否则会导致深度计算错误。
内容的提问来源于stack exchange,提问作者AndreyR
相关产品推荐
相关产品推荐

