JSON.stringify抛出RangeError:大型对象的字符串长度无效

JSON.stringify throws RangeError: Invalid string length for huge objects

本文关键字:字符串 无效 对象 大型 stringify 抛出 RangeError JSON      更新时间:2023-09-26

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

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

知道如何解决这个问题吗?

我也看到了这个无益/误导性的nodejs错误消息,所以我在nodejs github 上预订了一个问题

RangeError:无效的字符串长度--应该是内存不足

正如@sandepanu所提到的,@madhuninmo有一个很好的小解决方案,适用于您试图字符串化一个巨大的数组。一次只字符串化一个元素:

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();

我发现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");
    }
);

从这里获取了示例代码。

这里有一个简单的帮助文件可以完成这项工作:

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
}