基于自定义ISerializer<T>适配System.Text.JSON的序列化边界问题
解决方案:适配ISerializer的高效JSON序列化实现
你的核心问题是System.Text.Json序列化单个值时没有明确边界,导致连续序列化的数值(如123和456)会拼接成无分隔的字节流,反序列化时无法正确拆分。用Boxed<T>会带来额外的序列化开销,下面提供两种更优方案:
方案一:长度前缀标记(推荐,通用且高效)
通过在序列化数据前写入长度前缀,反序列化时先读取长度,再精准截取对应长度的字节流进行解析。这种方式几乎无性能开销,且适用于所有类型(包括复杂对象)。
实现代码
class JsonSerializer<T> : ISerializer<T> { public void Serialize(T value, IBufferWriter<byte> writer) { // 先将对象序列化到临时缓冲区,获取字节长度 using var ms = new MemoryStream(); using var jsonWriter = new Utf8JsonWriter(ms); System.Text.Json.JsonSerializer.Serialize(jsonWriter, value); jsonWriter.Flush(); var serializedBytes = ms.ToArray(); // 写入VarInt编码的长度前缀(比固定4字节更省空间) WriteVarInt(writer, serializedBytes.Length); // 写入序列化后的实际数据 writer.Write(serializedBytes); } public T Deserialize(ref SequenceReader<byte> source) { // 读取长度前缀 if (!TryReadVarInt(ref source, out int dataLength)) { throw new InvalidDataException("无法读取数据长度前缀"); } // 截取对应长度的字节序列 if (!source.TryReadExact(dataLength, out ReadOnlySequence<byte> targetData)) { throw new InvalidDataException("数据长度不足"); } // 反序列化目标数据 var reader = new Utf8JsonReader(targetData); return System.Text.Json.JsonSerializer.Deserialize<T>(ref reader)!; } // VarInt编码写入(小长度仅需1-2字节,空间效率更高) private void WriteVarInt(IBufferWriter<byte> writer, int value) { do { byte b = (byte)(value & 0x7F); value >>= 7; if (value != 0) { b |= 0x80; } writer.Write(new[] { b }); } while (value != 0); } // VarInt编码读取 private bool TryReadVarInt(ref SequenceReader<byte> source, out int result) { result = 0; int shift = 0; byte b; do { if (!source.TryRead(out b)) { return false; } result |= (b & 0x7F) << shift; shift += 7; if (shift > 31) { return false; } } while ((b & 0x80) != 0); return true; } }
优势
- 通用:支持所有
T类型,包括复杂对象、字符串(即使包含特殊字符) - 高效:VarInt编码的长度前缀空间开销极小,序列化/反序列化的性能损耗可以忽略
- 可靠:完全避免边界混淆问题,每个序列化单元的边界清晰
方案二:分隔符标记(仅适用于简单类型)
如果你的T仅为简单类型(如int、bool、不含特殊分隔符的字符串),可以在序列化数据末尾写入分隔符(如换行符\n),反序列化时读到分隔符就停止解析。
实现代码
class JsonSerializer<T> : ISerializer<T> { private const byte Delimiter = (byte)'\n'; public void Serialize(T value, IBufferWriter<byte> writer) { var jsonWriter = new Utf8JsonWriter(writer); System.Text.Json.JsonSerializer.Serialize(jsonWriter, value); jsonWriter.Flush(); // 写入分隔符 writer.Write(new[] { Delimiter }); } public T Deserialize(ref SequenceReader<byte> source) { // 找到分隔符位置,不提前跳过 if (!source.TryReadTo(out ReadOnlySequence<byte> data, Delimiter, advancePastDelimiter: false)) { throw new InvalidDataException("未找到数据分隔符"); } // 反序列化目标数据 var reader = new Utf8JsonReader(data); var result = System.Text.Json.JsonSerializer.Deserialize<T>(ref reader)!; // 跳过数据和分隔符 source.Advance(data.Length + 1); return result; } }
注意事项
- 仅适用于
T的序列化结果中不包含分隔符的场景,比如int、bool;如果是包含换行符的字符串,会导致错误截断 - 实现简单,性能比长度前缀方案略优,但局限性大
内容的提问来源于stack exchange,提问作者Bogey
相关产品推荐
相关产品推荐

