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

使用JsonSerializer与MemoryMappedViewStream时触发JSON无效0x00错误

解决MemoryMappedViewStream配合System.Text.Json反序列化时的0x00无效字符异常

问题根源

MemoryMappedViewStream基于固定大小的内存区域,序列化完成后,流的剩余未写入部分会被填充为0字节。而System.Text.Json的反序列化器会读取整个流直到结束,当遇到这些末尾的0字节时,会判定为无效的JSON字符,从而抛出'0x00' is invalid after a single JSON value异常。

普通MemoryStream会跟踪实际写入的字节长度,反序列化时只会读取有效数据,因此不会触发该问题。

解决方案

核心思路是让反序列化器仅处理实际写入的JSON有效数据,以下是两种可行方案:

方案一:先序列化到字节数组,记录长度后写入流

先将对象序列化为UTF-8字节数组,记录数组长度并写入流,反序列化时先读取长度,再读取对应长度的字节进行反序列化:

string dataFilePath = Guid.NewGuid().ToString();
using (MemoryMappedFile dataFile = MemoryMappedFile.CreateNew(dataFilePath, 104857600, MemoryMappedFileAccess.ReadWrite))
using (MemoryMappedViewStream dataStream = dataFile.CreateViewStream())
{
    SimpleDataSource original = new SimpleDataSource
    {
        Name = "Example Name",
        Date = new DateTime(1999, 12, 31),
        Number = 191
    };

    // 序列化为字节数组,获取有效数据长度
    byte[] jsonBytes = JsonSerializer.SerializeToUtf8Bytes(original);
    // 先写入长度(用4字节int存储)
    dataStream.Write(BitConverter.GetBytes(jsonBytes.Length), 0, 4);
    // 写入JSON数据
    dataStream.Write(jsonBytes, 0, jsonBytes.Length);
    
    dataStream.Seek(0, SeekOrigin.Begin);
    
    // 读取长度
    byte[] lengthBuffer = new byte[4];
    dataStream.Read(lengthBuffer, 0, 4);
    int jsonLength = BitConverter.ToInt32(lengthBuffer, 0);
    // 读取对应长度的JSON字节
    byte[] jsonReadBytes = new byte[jsonLength];
    dataStream.Read(jsonReadBytes, 0, jsonLength);
    // 反序列化
    SimpleDataSource deserialized = JsonSerializer.Deserialize<SimpleDataSource>(jsonReadBytes);
}

方案二:自定义限定长度的Stream包装器

通过自定义Stream类限制读取的字节数,确保反序列化器只处理有效数据:

string dataFilePath = Guid.NewGuid().ToString();
using (MemoryMappedFile dataFile = MemoryMappedFile.CreateNew(dataFilePath, 104857600, MemoryMappedFileAccess.ReadWrite))
using (MemoryMappedViewStream dataStream = dataFile.CreateViewStream())
{
    SimpleDataSource original = new SimpleDataSource
    {
        Name = "Example Name",
        Date = new DateTime(1999, 12, 31),
        Number = 191
    };

    // 记录序列化前的起始位置
    long startPos = dataStream.Position;
    JsonSerializer.Serialize(dataStream, original);
    // 计算实际写入的字节数
    long bytesWritten = dataStream.Position - startPos;
    
    dataStream.Seek(startPos, SeekOrigin.Begin);
    
    // 使用自定义限定流读取有效数据
    using (var limitedStream = new LimitedStream(dataStream, bytesWritten))
    {
        SimpleDataSource deserialized = JsonSerializer.Deserialize<SimpleDataSource>(limitedStream);
    }
}

// 自定义限定读取长度的Stream类
public class LimitedStream : Stream
{
    private readonly Stream _innerStream;
    private long _remainingBytes;

    public LimitedStream(Stream innerStream, long length)
    {
        _innerStream = innerStream;
        _remainingBytes = length;
    }

    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 int Read(byte[] buffer, int offset, int count)
    {
        int bytesToRead = (int)Math.Min(count, _remainingBytes);
        if (bytesToRead <= 0) return 0;
        int bytesRead = _innerStream.Read(buffer, offset, bytesToRead);
        _remainingBytes -= bytesRead;
        return bytesRead;
    }

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

说明

两种方案都能有效避免读取内存映射流末尾的空字节,方案一实现更简洁,适合大多数场景;方案二无需提前序列化到字节数组,适合大对象的序列化场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 14:45:10