node.js 流数组 json 以响应
node.js stream array of json to response
我有一个 REST 方法,它应该 return 一个 JSON 数组,其中包含从 mongodb 中读取的某些元素(使用 mongoose)。
应该很简单(在实际情况下 find 方法有参数,但这不是问题):
OutDataModel.find().stream({transform: JSON.stringify}).pipe(res);
这种方法的问题是我没有得到有效的 JSON,因为结果是这样的:
{"id":"1","score":11}{"id":"2","score":12}{"id":"3","score":13}
而我期待的是:
[{"id":"1","score":11},{"id":"2","score":12},{"id":"3","score":13}]
我还没有找到解决方案,但我很确定会有一个简单的解决方案。
我试过的:
没有什么值得我骄傲的,但就是这样。
- 在流式传输之前将
'['
写入响应。
- 而不是 JSON.stringify 我提供了另一种调用 JSON.stringify 并在末尾添加
','
的方法
- 在流的 'end' 事件中,我将
']'
写入响应。
仍然无法使用此 "solution",因为我在末尾有一个逗号,如下所示:
[{"id":"1","score":11},{"id":"2","score":12},{"id":"3","score":13},]
正如我所说,我很确定应该有一个干净的解决方案,因为它应该很常见。
这个方法会有很多并发调用,所以我不想把所有的东西都读入内存,然后把所有的东西都写到响应中。每次调用不会 return 很多记录,但它们加在一起可能会很大。消费者是一个 java 应用程序 spring,使用 jackson 解析 JSON.
请告诉我怎么做。
回答
我按照接受的答案中的建议创建了一个转换流,让它工作了。
我的流看起来像这样:
var arraystream = new stream.Transform({objectMode: true});
arraystream._hasWritten = false;
arraystream._transform = function (chunk, encoding, callback) {
console.log('_transform:' + chunk);
if (!this._hasWritten) {
this._hasWritten = true;
this.push('[' + JSON.stringify(chunk));
} else {
this.push(',' + JSON.stringify(chunk));
}
callback();
};
arraystream._flush = function (callback) {
console.log('_flush:');
this.push(']');
callback();
};
以及使用它的代码:
OutDataModel.find().stream().pipe(arraystream).pipe(res);
谢谢。
通过实施您自己的逻辑,您走在了正确的轨道上。你也可以在这里使用 ArrayFormatter
做类似的事情:https://gist.github.com/aheckmann/1403797
转换函数在每个文档上单独调用——Mongoose QueryStream 为每个 'data' 事件发出一个文档,但 QueryStream 不会在语义上将它们视为任何更大的数组数据结构的一部分;要获得数组格式的 JSON,您确实必须自己做(正如您推测的那样)。
我在这里找到了一个非常简单干净的解决方案:
这是我的简化版代码:
Products
.find({})
.lean()
.stream({
transform: () => {
let index = 0;
return (data) => {
return (!(index++) ? '[' : ',') + JSON.stringify(data);
};
}() // invoke
})
.on('end', () => {
res.write(']');
})
.pipe(res);
我有一个 REST 方法,它应该 return 一个 JSON 数组,其中包含从 mongodb 中读取的某些元素(使用 mongoose)。
应该很简单(在实际情况下 find 方法有参数,但这不是问题):
OutDataModel.find().stream({transform: JSON.stringify}).pipe(res);
这种方法的问题是我没有得到有效的 JSON,因为结果是这样的:
{"id":"1","score":11}{"id":"2","score":12}{"id":"3","score":13}
而我期待的是:
[{"id":"1","score":11},{"id":"2","score":12},{"id":"3","score":13}]
我还没有找到解决方案,但我很确定会有一个简单的解决方案。
我试过的:
没有什么值得我骄傲的,但就是这样。
- 在流式传输之前将
'['
写入响应。 - 而不是 JSON.stringify 我提供了另一种调用 JSON.stringify 并在末尾添加
','
的方法 - 在流的 'end' 事件中,我将
']'
写入响应。
仍然无法使用此 "solution",因为我在末尾有一个逗号,如下所示:
[{"id":"1","score":11},{"id":"2","score":12},{"id":"3","score":13},]
正如我所说,我很确定应该有一个干净的解决方案,因为它应该很常见。
这个方法会有很多并发调用,所以我不想把所有的东西都读入内存,然后把所有的东西都写到响应中。每次调用不会 return 很多记录,但它们加在一起可能会很大。消费者是一个 java 应用程序 spring,使用 jackson 解析 JSON.
请告诉我怎么做。
回答
我按照接受的答案中的建议创建了一个转换流,让它工作了。
我的流看起来像这样:
var arraystream = new stream.Transform({objectMode: true});
arraystream._hasWritten = false;
arraystream._transform = function (chunk, encoding, callback) {
console.log('_transform:' + chunk);
if (!this._hasWritten) {
this._hasWritten = true;
this.push('[' + JSON.stringify(chunk));
} else {
this.push(',' + JSON.stringify(chunk));
}
callback();
};
arraystream._flush = function (callback) {
console.log('_flush:');
this.push(']');
callback();
};
以及使用它的代码:
OutDataModel.find().stream().pipe(arraystream).pipe(res);
谢谢。
通过实施您自己的逻辑,您走在了正确的轨道上。你也可以在这里使用 ArrayFormatter
做类似的事情:https://gist.github.com/aheckmann/1403797
转换函数在每个文档上单独调用——Mongoose QueryStream 为每个 'data' 事件发出一个文档,但 QueryStream 不会在语义上将它们视为任何更大的数组数据结构的一部分;要获得数组格式的 JSON,您确实必须自己做(正如您推测的那样)。
我在这里找到了一个非常简单干净的解决方案:
这是我的简化版代码:
Products
.find({})
.lean()
.stream({
transform: () => {
let index = 0;
return (data) => {
return (!(index++) ? '[' : ',') + JSON.stringify(data);
};
}() // invoke
})
.on('end', () => {
res.write(']');
})
.pipe(res);