Java 使用Jackson避免内存收集流式处理内部JSONArray记录
最近,我一直试图深入研究如何通过HTTP端点传输JSON内容,因为我不想在发送响应之前将整个JSON存储在内存中 然而,对于如何做到这一点,以及我以前的JSON模式是否必须改变以应对流式传输和以后的解析,我有一些疑问 目前我所做的(并且仍然希望做流媒体)如下Java 使用Jackson避免内存收集流式处理内部JSONArray记录,java,json,jackson,streaming,Java,Json,Jackson,Streaming,最近,我一直试图深入研究如何通过HTTP端点传输JSON内容,因为我不想在发送响应之前将整个JSON存储在内存中 然而,对于如何做到这一点,以及我以前的JSON模式是否必须改变以应对流式传输和以后的解析,我有一些疑问 目前我所做的(并且仍然希望做流媒体)如下 { "content": [ // I don't want to wait for this array to be populated to flush it. {
{
"content": [ // I don't want to wait for this array to be populated to flush it.
{
"key": "value",
"yet": "another_value"
}, // read individual nested record, flush it
{
"key": "valueagain",
"yet": "another_value_in_here"
} // read individual nested record, flush it
],
"random": {
"more": "fields",
"in": {
"here": "yeah"
}
}
}
我的疑问是,有没有一种方法(使用Jackson或任何其他Java库)可以在不等待关闭给定键的值的情况下流式传输JSON的内部部分
假设我想开始流媒体
{
"content": [
...
然后接下来的所有内容
{
"key": "value",
"yet": "another_value"
}
或者,这样做的方式是将总体结果更改为这个
[
{
"key": "value",
"yet": "another_value"
},
{
"key": "valueagain",
"yet": "another_value_in_here"
},
{
"more": "fields",
"in": {
"here": "yeah"
}
}
]
因此,基本上,如果有一个jsonggenerator
,我是否能够像我的示例中那样刷新不完整的数组
jGenerator.writeStartObject();
jGenerator.writeFieldName("content");
jGenerator.writeStartArray();
jGenerator.flush(); // initial stream, records incoming within next flush. Can this be streamed?
jGenerator.writeStartObject();
jGenerator.writeStringField("key", "value");
jGenerator.writeStringField("yet", "another_value");
jGenerator.writeEndObject();
jGenerator.flush(); // streaming individual JSON object
... so on so forth until array is complete ...
jGenerator.writeEndArray();
jsonGenerator.writeFieldName("random");
如果您可以获得Json的输入流,而无需将全部内容放在内存中。例如,您可以将其保存在硬盘上,只需从
文件获取InputStream
有了jackson,你可以有这样的体验:
private final JsonParser jp;
public JsonArrayInputStreamWrapper(InputStream inputStream) throws IOException {
this.jp = new JsonFactory().createParser(inputStream);
setUpCursorToContentArray();
}
private void setUpCursorToContentArray() throws IOException {
// validate if json contains "content" key
do {
if (jp.nextToken() == null)
throw new JsonException("Provided input doesn't contain content.");
} while (!"content".equals(jp.getText()));
// set up coursor on first array that is found after "content"
while (jp.nextToken() != JsonToken.START_ARRAY) {
if (jp.currentToken() == null)
// throw exception when there weren't any array
throw new JsonException("Content input is malformed.");
}
}
所以我的想法是将coursor设置在我希望在这个所谓包装器的构造函数中开始处理的地方。这样,在成功初始化对象后,我可以使用hasNext()
和next()
进行更改
@Override
public boolean hasNext() {
if(jp.isClosed()) {
// has to be handled
}
return jp.nextToken() == JsonToken.START_OBJECT
}
@Override
public Map next() {
// where mapper is jackson ObjectMapper
return mapper.readValue(jp, Map.class);
}
这可用于以下情况:
while (wrapper.hasNext()) {
var inv = wrapper.next();
//processing
}
完成处理后,不要忘记jp.close()
。JSON是文本,因此流式处理和刷新的顺序将与屏幕上显示的顺序完全相同。开始括号写在结束括号之前。除非在传输字符串之前先显式构建字符串,否则流式传输不是问题,否则您只需将JSON生成为outputstream(或其特定于框架的等效内容)。