Utf8JsonWriter 适合什么场景
需要高性能、低内存占用地生成 JSON 字符串(比如 API 响应流式输出、日志结构化、大数据量导出),且不依赖对象序列化逻辑——
Utf8JsonWriter是纯写入器,不反射、不缓存、不构建中间对象树。
它直接向
Stream或
Span<byte></byte>写入 UTF-8 字节,跳过字符串编码/解码开销。如果你只是拼个 JSON 片段或配合
HttpResponse.Body边写边发,它比
JsonSerializer.Serialize快 2–5 倍,GC 压力极低。
初始化时必须注意缓冲区和编码
Utf8JsonWriter默认使用
Stream构造函数,但若传入未设置
CanWrite == true的流(如只读的
MemoryStream视图)、或未预分配足够容量的
MemoryStream,会在写入中途抛
IOException或触发频繁扩容。 写入到响应体:直接传
context.Response.Body,确保响应头已设
"Content-Type: application/json; charset=utf-8"写入到内存:用
new MemoryStream(4096)预分配缓冲,避免小块多次分配;完成后调用
stream.Position = 0再读取 绝对不要传
Encoding.UTF8.GetBytes("...") 得到的字节数组封装成 ReadOnlyMemory<byte></byte>——
Utf8JsonWriter只接受可写的
IBufferWriter<byte></byte>或
Stream
写入嵌套结构必须严格配对 Begin/End 方法
没有自动补全,漏掉一个
WriteEndArray()就会产出非法 JSON,且运行时不报错,只在下游解析时报
JsonReaderException: Unexpected end of input。
常见错误模式:
循环中写对象但忘了在循环外WriteStartArray()和
WriteEndArray()条件分支里写了
WriteStartObject(),但某些分支没写对应
WriteEndObject()调用
WritePropertyName("items") 后,误用 WriteString("value") 而不是 WriteStartArray()+ 子项 +
WriteEndArray()
示例(正确嵌套):
writer.WriteStartObject();
writer.WriteString("name", "Alice");
writer.WriteStartArray("hobbies");
writer.WriteString("reading");
writer.WriteString("coding");
writer.WriteEndArray(); // ← 必须有
writer.WriteEndObject(); // ← 必须有
字符串和数值写入要选对重载方法
WriteString(string)和
WriteString(ReadOnlySpan<char>)</char>性能差一倍以上;后者避免字符串分配,但需确保 span 生命周期覆盖写入过程。数值同理:
WriteNumber(int)比
WriteString(int.ToString())快且安全(无文化相关格式风险)。 字段名永远用
WritePropertyName(string),不要用
WriteString()模拟 写原始 JSON 片段(如已有合法 JSON 字符串)用
WriteRawValue(string, skipInputValidation: true),但必须确保内容本身是合法 UTF-8 JSON 写
null用
WriteNull(),别写
WriteString("null")
时间类型建议转为 ISO 8601 字符串后用 WriteString(),
Utf8JsonWriter不内置日期格式化 写完记得
Flush()(尤其写入
Stream时),否则最后几个字节可能卡在缓冲区里发不出去。另外,它不处理换行缩进——需要美化输出就得自己加
JsonWriterOptions并设
Indented = true,但这会明显降低性能。
