网站搜索

node.js - 以内存高效的方式从 JSON 中过滤无效的 JSON 值


我们有一些我们正在尝试解析的错误 JSON。不幸的是,它不是有效的 JSON,因为它在负载中返回未加引号的 NaN。

我们正在从已经过时很久的 request 库切换到 axios。这似乎使我们当前修复此负载的方法的内存使用量翻倍,但我们的环境内存受限。文件大小为 19MB,我们的限制是 50MB。我推测某些与正则表达式/解析相关的操作导致在内存中又生成了 JSON 的另一个副本,此外 axios 自身还在尝试进行解析。

我们正在使用一个围绕 axios 的封装,因此直接与它交互是有限的。我必须重新实现封装的部分内容才能访问它,而且它是一个外部内部库。

我知道我们试图保留的键,所以实际上丢弃其余结构而不是处理 NaN 更可取。

我们想要的结构看起来像 DataUsages[]

export interface DataUsages {
    dataUsageId: string;
    dataUsageName: string;
}

我们得到的结构在数组的对象中有额外的项目,我们根本不关心 "dataUsageDownstreamUsages",而且这个键可以被丢弃,也确实被丢弃了。

[
  {
    "dataUsageId": "42",
    "dataUsageName": "myname",
    "dataUsageDownstreamUsages": [NaN]
  }
]

当前

这是我们目前的方法

      const reg1 = /\[NaN]|NaN/gm
      const parsed: EDMDataUsages[] = JSON.parse(
        requireNonNullish(response.body, 'body').replace(reg1, '""').replace(/NAN/gm, ''),
      )   

流式 JSON

现在我正在考虑为此使用 stream-json。

如果我使用解析器,它会中止,大概是在遇到 NaN 时。所以我在看 disassembler,但我不明白如何用它来做这件事。

  const read = new Readable()
  read.push(requireNonNullish(response.body, 'body'))
  read.push(null)

  const pipeline = chain([read, disassembler(), pick({ filter: 'data' }), data => this.log.trace('data', data)])
  pipeline.on('data', data => this.log.trace('filter', data))

显然这段代码不完整。

其他库也是可以的。请提供一个完整的示例。

答案#1

好,首先让我们创建一个假的数据

function genreateFakeData() {
    const payload = { res: [] }
    const mock = {
        "dataUsageId": "42",
        "dataUsageName": "myname",
        "dataUsageDownstreamUsages": [NaN]
    }
    let totalFileSize = 0;
    const fileSize = 19;
    const SIZE_IN_BYTES = JSON.stringify(mock).length;
    const SIZE_IN_MB = SIZE_IN_BYTES / 1024 / 1024;

    while (totalFileSize < fileSize) {
        totalFileSize += SIZE_IN_MB;
        payload.res.push(mock);
    }
    fs.appendFileSync('fake.json', JSON.stringify(payload));
}

那么让我们测试一下这是不是文件的正确大小

> ls -ltrh | grep fake
-rw-r--r--   1 naor.tedgi  staff    19M Jun 15 09:34 fake.json

让我们在堆内存限制下运行节点应用,并更改原始响应

node --max-old-space-size=50 index.js

index.js

const stream = require('stream');
const fs = require('fs');
const data = JSON.parse(fs.readFileSync('fake.json',{encoding:'utf8', flag:'r'}));
data.res.forEach(entry=>{
    delete entry.dataUsageDownstreamUsages
})
console.log(data)