Потоковая внутренняя запись JSONArray с Джексоном, избегая сбора памяти - PullRequest
0 голосов
/ 20 февраля 2020

Недавно пытался разобраться, как передавать JSON контент через конечную точку HTTP, так как я не хочу сохранять весь 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"
            }
        }
    }

Я сомневаюсь, есть ли способ (с использованием Джексона или любой другой Java lib) для потоковой передачи внутренних частей 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, смогу ли я обработать sh неполных массивов, таких как в моем примере?

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

1 Ответ

0 голосов
/ 20 февраля 2020

Если вы можете получить входной поток на ваш Json без помещения всего содержимого в память. Например, вы можете сохранить его на жестком диске и просто получить InputStream из File.

С Джексоном вы можете получить что-то вроде этого:

  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() после окончания обработки sh.

...