Java 使用Jackson避免内存收集流式处理内部JSONArray记录

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. {

最近,我一直试图深入研究如何通过HTTP端点传输JSON内容,因为我不想在发送响应之前将整个JSON存储在内存中

然而,对于如何做到这一点,以及我以前的JSON模式是否必须改变以应对流式传输和以后的解析,我有一些疑问

目前我所做的(并且仍然希望做流媒体)如下

    {
        "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(或其特定于框架的等效内容)。