必须用jsondocument.parse()并显式.dispose(),因其内部持有未托管内存,不释放会导致内存泄漏;它适合只取少量字段、结构动态或性能敏感场景,且需用trygetproperty等安全访问方式。

直接用 JsonDocument.Parse() 就行,但必须 .Dispose(),否则内存不释放——这不是建议,是硬性要求。
为什么必须用 JsonDocument 而不是强类型反序列化?
当你只取 JSON 中一两个字段(比如日志里抽 "error.code" 或 API 响应里的 "data.items[0].id"),又不想定义一整套类、也不愿承担 JsonSerializer.Deserialize<t>()</t> 因字段缺失或类型错配导致的 JsonException 时,JsonDocument 是最轻量、最安全的选择。
它不走反射、不构造对象、不验证字段完整性,纯靠游标遍历,解析快、内存稳。适合:调试探查、API 响应结构未完全确定、配置项可选、日志字段提取等场景。
- 结构动态:JSON 里
"data"有时是对象,有时是数组,有时还可能为null - 字段稀疏:你只关心
"timestamp"和"level",其余几十个字段根本不想碰 - 性能敏感:单次请求要解析上百个响应体,且每次只读 1–2 个字段
JsonDocument 必须 .Dispose() 的真实原因
JsonDocument 内部持有未托管内存(底层用 Utf8JsonReader + 缓冲池),不显式释放会持续占用,尤其在高并发 Web API 中极易引发内存泄漏——现象是 GC 不回收、Private Bytes 持续上涨、最终 OutOfMemoryException。
常见错误写法:
var doc = JsonDocument.Parse(json); // ❌ 没 Dispose
string id = doc.RootElement.GetProperty("id").GetString();
// 方法结束,doc 变量出作用域,但内存没释放
正确姿势(三选一):
- 用
using语句块:using var doc = JsonDocument.Parse(json); - 手动调
doc.Dispose()(确保在finally里) - 异步流式解析时用
JsonDocument.ParseAsync(stream),同样需await using
访问字段前务必用 TryGetProperty()
RootElement.GetProperty("xxx") 在字段不存在时直接抛 KeyNotFoundException,而生产环境 JSON 结构常有波动(比如后端加了新字段、旧字段临时下线、灰度发布差异),硬写 GetProperty 等于主动埋雷。
安全写法示例:
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (root.TryGetProperty("user", out var userElem) &&
userElem.TryGetProperty("profile", out var profileElem) &&
profileElem.TryGetProperty("email", out var emailElem)) {
string email = emailElem.GetString();
}
- 每个
TryGetProperty都返回bool,链式判断天然防崩 - 不要依赖
HasProperty("xxx")再GetProperty——两次查找,性能差且非原子 - 数组索引同理:先
elem.ValueKind == JsonValueKind.Array,再用elem.GetArrayLength()判空,最后用elem[0]或TryGetArrayLength(out int len)
大文件或流式场景别用 Parse(),改用 ParseAsync()
JsonDocument.Parse(string) 会把整个字符串加载进内存再解析,遇到几 MB 的 JSON 文件(如导出报表、原始日志包),极易触发 OutOfMemoryException,尤其在低内存容器或 Serverless 环境中。
替代方案:
- 文件太大 → 用
FileStream+JsonDocument.ParseAsync(stream) - HTTP 响应体 → 直接传
response.Content.ReadAsStream()进去,避免ReadAsStringAsync()先全读进内存 - 注意:流必须支持
Seek(MemoryStream行,NetworkStream不行),不支持时得先 CopyTo 临时MemoryStream
真正容易被忽略的点:很多人以为“只读”就等于“无副作用”,但 JsonDocument 的“只读”仅指不可修改节点值;它的内存生命周期、字段存在性检查逻辑、以及对非法 JSON 的容忍度(比如不处理注释),全都需要你亲手兜底——漏掉 Dispose 或绕过 TryGetProperty,问题不会立刻报,但会在压测或上线后某个深夜准时出现。











