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

2次阅读

utf8jsonwriter 适合高性能、低内存占用的 json 生成场景,如 api 流式响应、结构化日志和大数据导出;它直接写入 utf-8 字节,无反射、无中间对象,比 jsonserializer 快 2–5 倍且 gc 压力极低。

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

Utf8JsonWriter 适合什么场景

需要高性能、低内存占用地生成 JSON 字符串(比如 API 响应流式输出、日志结构化、大数据量导出),且不依赖对象序列化逻辑——Utf8JsonWriter 是纯写入器,不反射、不缓存、不构建中间对象树。

它直接向 StreamSpan<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
  • NULLWriteNull(),别写 WriteString("null")
  • 时间类型建议转为 ISO 8601 字符串后用 WriteString()Utf8JsonWriter 不内置日期格式化

写完记得 Flush()(尤其写入 Stream 时),否则最后几个字节可能卡在缓冲区里发不出去。另外,它不处理换行缩进——需要美化输出就得自己加 JsonWriterOptions 并设 Indented = true,但这会明显降低性能。

text=ZqhQzanResources