JSON 流式解析:处理 GB 级 JSON 数据

深入讲解 JSON 流式解析的原理和实现。学习如何处理超大 JSON 文件,避免内存溢出。

JSON 工具 2026-05-30 8 分钟

JSON 流式解析:处理 GB 级 JSON 数据

当 JSON 文件达到 GB 级别时,传统的一次性解析方式会导致内存溢出。流式解析是解决这个问题的关键技术。

传统解析 vs 流式解析

传统解析(一次性加载)

// 整个文件加载到内存
const data = JSON.parse(fs.readFileSync('huge.json', 'utf8'));
// 内存占用:文件大小 × 2-3 倍

流式解析(逐块处理)

// 逐条读取,内存占用极低
const stream = fs.createReadStream('huge.json')
  .pipe(JSONStream.parse('*'))
  .on('data', item => {
    // 每次只处理一条数据
    processItem(item);
  });

流式解析原理

流式解析器(Streaming Parser)不构建完整的数据树,而是:

  1. 逐字符读取 JSON
  2. 识别 token({、}、[、]、:、,、字符串、数字)
  3. 触发事件回调
  4. 处理完即释放内存

JavaScript 流式解析

使用 stream-json

const { parser } = require('stream-json');
const { streamArray } = require('stream-json/streamers/StreamArray');
const { createReadStream } = require('fs');

const pipeline = createReadStream('data.json')
  .pipe(parser())
  .pipe(streamArray());

pipeline.on('data', ({ key, value }) => {
  console.log(`Item ${key}:`, value);
});

pipeline.on('end', () => {
  console.log('处理完成');
});

使用 oboe.js(前端)

oboe('/api/large-data')
  .node('items.*', function(item) {
    // 每收到一条数据就处理
    renderItem(item);
  })
  .done(function(data) {
    console.log('全部加载完成');
  });

Java 流式解析

Jackson Streaming API

JsonFactory factory = new JsonFactory();
try (JsonParser parser = factory.createParser(new File("data.json"))) {
    while (parser.nextToken() != null) {
        if (parser.currentToken() == JsonToken.START_OBJECT) {
            // 读取单个对象
            MyObject obj = parser.readValueAs(MyObject.class);
            processObject(obj);
        }
    }
}

逐字段读取(最省内存)

try (JsonParser parser = factory.createParser(new File("data.json"))) {
    String currentField = null;
    while (parser.nextToken() != null) {
        if (parser.currentToken() == JsonToken.FIELD_NAME) {
            currentField = parser.getCurrentName();
        } else if ("name".equals(currentField)) {
            String name = parser.getText();
            // 只提取需要的字段
        }
    }
}

Python 流式解析

import ijson

# 流式读取大 JSON 文件
with open('data.json', 'rb') as f:
    # 只读取 items 数组中的每个元素
    for item in ijson.items(f, 'items.item'):
        process_item(item)

JSON Lines 格式

JSON Lines 是流式处理的友好格式:

{"id": 1, "name": "Alice"}
{"id": 2, "name": "Bob"}
{"id": 3, "name": "Charlie"}

优势:

  • 每行独立,可以逐行处理
  • 支持 append 写入
  • 可以用 grep、awk 处理
  • 天然支持并行处理

性能对比

对 1GB JSON 文件的处理测试:

方法 内存占用 处理时间
JSON.parse 4.2GB 45s
流式解析 50MB 60s
JSON Lines 10MB 35s

最佳实践

  1. 设计 API 时考虑流式:返回 JSON Lines 或支持分页
  2. 选择合适的库:JavaScript 用 stream-json,Java 用 Jackson Streaming
  3. 只提取需要的字段:避免加载不必要的数据
  4. 设置超时和重试:大文件处理可能超时

总结

流式解析是处理大数据量 JSON 的关键技术。通过逐块读取和处理,可以在有限的内存中处理无限大的 JSON 文件。

📚 相关文章