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

如何正确反序列化AWS S3流返回的首尾带双引号的JSON字符串?

问题原因

你获取到的流内容本质是经过两次JSON序列化的结果:原本的JSON数组先被序列化为字符串,随后这个字符串又被作为值做了一次JSON序列化,因此多了首尾的双引号,内部的双引号也被加上了转义符。

方案1:自定义包装流实现流式处理(最优,适配大文件场景)

完全不破坏你原有流式读取的逻辑,无需加载全量内容到内存,仅通过自定义流跳过首尾的双引号即可,代码改动最小:

// 自定义包装流,自动跳过首尾的双引号字符
public class TrimQuotesStream : Stream
{
    private readonly Stream _innerStream;
    private bool _firstCharRead = false;
    private bool _lastCharChecked = false;
    private int _lastChar = -1;

    public TrimQuotesStream(Stream innerStream)
    {
        _innerStream = innerStream ?? throw new ArgumentNullException(nameof(innerStream));
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int bytesRead = _innerStream.Read(buffer, offset, count);
        if (bytesRead == 0)
        {
            // 处理最后一个字符如果是双引号的情况
            if (!_lastCharChecked && _lastChar == '"')
            {
                _lastCharChecked = true;
                return 0;
            }
            return 0;
        }

        int start = offset;
        // 跳过第一个字符如果是双引号
        if (!_firstCharRead)
        {
            _firstCharRead = true;
            if (buffer[offset] == '"')
            {
                start++;
                bytesRead--;
                if (bytesRead == 0) return Read(buffer, offset, count);
            }
        }

        // 缓存最后一个字符,下次读取时判断是否是双引号
        if (!_lastCharChecked)
        {
            if (_lastChar != -1)
            {
                buffer[start - 1] = (byte)_lastChar;
                start--;
                bytesRead++;
            }
            _lastChar = buffer[offset + bytesRead - 1];
            bytesRead--;
        }

        return bytesRead;
    }

    // 其他Stream方法直接代理到内部流即可
    public override bool CanRead => _innerStream.CanRead;
    public override bool CanSeek => _innerStream.CanSeek;
    public override bool CanWrite => _innerStream.CanWrite;
    public override long Length => _innerStream.Length;
    public override long Position { get => _innerStream.Position; set => _innerStream.Position = value; }
    public override void Flush() => _innerStream.Flush();
    public override long Seek(long offset, SeekOrigin origin) => _innerStream.Seek(offset, origin);
    public override void SetLength(long value) => _innerStream.SetLength(value);
    public override void Write(byte[] buffer, int offset, int count) => _innerStream.Write(buffer, offset, count);
    protected override void Dispose(bool disposing)
    {
        if (disposing) _innerStream.Dispose();
        base.Dispose(disposing);
    }
}

你原有代码仅需要修改StreamReader的输入即可,其他逻辑完全不用动:

using (StreamReader sr = new StreamReader(new TrimQuotesStream(stream)))
using (JsonTextReader reader = new JsonTextReader(sr))
{
    // 原有逻辑保持不变
    reader.SupportMultipleContent = true;
    JsonSerializer serializer = new JsonSerializer();
    while (reader.Read())
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            var obj = serializer.Deserialize<ControllerEventJson>(reader);
            // 后续处理obj
        }
    }
}

方案2:基于现有JsonReader调整(适配中等大小文件)

如果不想自定义流,也可以利用JsonReader的特性先读取外层的字符串token,再对字符串内容做二次反序列化:

using (StreamReader sr = new StreamReader(stream))
using (JsonTextReader outerReader = new JsonTextReader(sr))
{
    // 读取外层的字符串token
    if (outerReader.Read() && outerReader.TokenType == JsonToken.String)
    {
        string innerJson = outerReader.Value.ToString();
        using (var innerSr = new StringReader(innerJson))
        using (var innerReader = new JsonTextReader(innerSr))
        {
            innerReader.SupportMultipleContent = true;
            JsonSerializer serializer = new JsonSerializer();
            while (innerReader.Read())
            {
                if (innerReader.TokenType == JsonToken.StartObject)
                {
                    var obj = serializer.Deserialize<ControllerEventJson>(innerReader);
                    // 后续处理obj
                }
            }
        }
    }
}

注意:该方案会把内部完整的JSON内容加载到内存中,不适合GB级以上的超大JSON文件。

方案3:小文件快速处理

如果S3对象体积很小,可以直接读取全量内容后修剪首尾双引号再处理:

using (StreamReader sr = new StreamReader(stream))
{
    string rawJson = sr.ReadToEnd().Trim('"');
    var result = JsonConvert.DeserializeObject<List<ControllerEventJson>>(rawJson);
    // 直接处理result列表即可
}

内容的提问来源于stack exchange,提问作者Jungl

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 03:18:00