You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 22:35:18