java - 使用 Jackson 流式传输内部 JSONArray 记录,避免内存收集

标签 java json jackson streaming

最近尝试深入研究如何通过 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"
    }
  }
]

那么,基本上,拥有一个 JsonGenerator,我是否能够刷新不完整的数组,例如我的示例中的数组?

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 的输入流,而无需将整个内容放入内存中。例如,您可以将其保存在 HDD 上,然后从 File 获取 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.");
    }
  }

所以我的想法是在我想要在这个所谓的包装器的构造函数中开始处理的地方设置我的游标。这样,在成功初始化对象后,我可以使用 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()

关于java - 使用 Jackson 流式传输内部 JSONArray 记录,避免内存收集,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60316503/

相关文章:

javascript - 如何对具有相似键的数据进行分组?

html - 更改特定数据的颜色

java - Jackson Mixin 无法在 Pojo 到 json 中工作

java - 如何在 java 中创建一个复杂的 json 对象?

java - 将 SortedList 比较器绑定(bind)到 TableView 比较器会删除排序

java - Web 浏览器中的 Thread.sleep 问题

java - Context 上的 Activity.this 引用

javascript - Node.js 从二维 JSON 数组中获取行

java - Spring - AWS(无 Spring Boot): java. lang.IllegalArgumentException:不是托管类型

java - 请求帮助解决 com.fasterxml.jackson.databind.JsonMappingException : Infinite recursion