C#流式写入JSON C#如何使用Utf8JsonWriter高效生成JSON

来源:这里教程网 时间:2026-02-21 17:42:16 作者:

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
,但这会明显降低性能。

相关推荐