JSON.stringify抛出了范围错误:对于超大对象,字符串长度无效

66

正如标题所示,我正在尝试使用JSON.stringify在我的Node.js应用程序中将巨大的JavaScript对象字符串化。这些对象非常庞大(数十兆字节),它们不包含任何函数。我需要将序列化后的对象写入文件。我现在得到的是:

RangeError: Invalid string length
  at Object.stringify (native)
  at stringifyResult (/my/file.js:123:45) -> line where I use JSON.stringify

有什么想法来解决这个问题吗?


5
如果你正在为输出准备数据结构,那么可以编写自己的 JSON 序列化器,逐步向输出流写入,而不是创建一个单一的大字符串。这并不是非常容易,但也不是非常困难。 - Pointy
4
我认为已经有流式的或缓冲的JSON序列化/反序列化器可用。 - user1106925
1
寻找类似的答案,但是针对JavaScript客户端(没有Node)。同时,这里有一个针对您的问题@boris的答案:https://dev59.com/ymAf5IYBdhLWcg3w52Ll - cregox
4个回答

29

2
还要检查:https://dev.to/madhunimmo/json-stringify-rangeerror-invalid-string-length-3977 - sandeepani

9

如@sandeepanu所述,如果您试图将一个巨大的数组字符串化,@madhunimmo提供了一个很好的小解决方案(链接)。只需逐个字符串化一个元素即可:

let out = "[" + yourArray.map(el => JSON.stringify(el)).join(",") + "]";

如果您想将具有大量键/属性的对象字符串化,那么您可以先使用Object.entries()将其转换为键/值对数组:
let out = "[" + Object.entries(yourObject).map(el => JSON.stringify(el)).join(",") + "]";

如果仍然无法解决问题,那么您可能需要使用流式处理方法,虽然您可以将数组切片成多个 jsonl(每行一个对象)文件进行存储。
// untested code
let numFiles = 4;
for(let i  = 0; i < numFiles; i++) {
  let out = arr.slice((i/numFiles)*arr.length, ((i+1)/numFiles)*arr.length).map(el => JSON.stringify(el)).join(",");
  // add your code to store/save `out` here
}

一种流式处理的方法(新的,目前仅在Chrome中支持,但很可能会在其他浏览器中以某种形式出现,甚至在Deno和Node.js中)是使用文件系统访问API。代码看起来像这样:

// untested code
const dirHandle = await window.showDirectoryPicker();
const fileHandle = await dirHandle.getFileHandle('yourData.jsonl', { create: true });
const writable = await fileHandle.createWritable();
for(let el of yourArray) {
  await writable.write(JSON.stringify(el)+"\n");
}
await writable.close();

6

我发现JSONStream是一个可靠的替代品,可以很好地处理大型对象,与原生的JSON.stringify一起使用。例如:

var fileSystem = require( "fs" );
var JSONStream = require( "JSONStream" );
var records = [
    { id: 1, name: "Terminator" },
    { id: 2, name: "Predator" },
    { id: 3, name: "True Lies" },
    { id: 4, name: "Running Man" },
    { id: 5, name: "Twins" }
    // .... hundreds of thousands of records ....
];

var transformStream = JSONStream.stringify();
var outputStream = fileSystem.createWriteStream( __dirname + "/data.json" );
transformStream.pipe( outputStream );    
records.forEach( transformStream.write );
transformStream.end();

outputStream.on(
    "finish",
    function handleFinish() {
        console.log("Done");
    }
);

这里获取示例代码。


这种方法对我很有帮助,谢谢。只有一个问题,这个答案能不能改进成单行JSON?因为在这个点上,每个JSON对象都会有一个换行符。 - Rodolfo Velasco

3
这里有一个简单的辅助文件可以完成这个任务:
const fs = require('fs');
const json = require('big-json');
 
// pojo will be sent out in JSON chunks written to the specified file name in the root 
function makeFile(filename, pojo){

    const stringifyStream = json.createStringifyStream({
        body: pojo
    });

    stringifyStream.on('data', function(strChunk) {
        fs.appendFile(filename, strChunk, function (err) {
            if (err) throw err;
        })
    });

}

module.exports = {
    makeFile
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接