如何正确反序列化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
相关产品推荐
相关产品推荐

