Рубрики
Без рубрики

Обрабатывать большие json-файлы с ограниченной памятью

Иногда нам нужно обработать большой файл json или поток, но нам не нужно хранить все содержимое в memo… Помеченный json, gson, java.

Иногда нам нужно обработать большой файл json или поток, но нам не нужно хранить все содержимое в памяти.

Например, когда мы подсчитываем количество элементов в большом массиве, нам просто нужно загрузить 1 элемент, увеличить количество, выбросить его и повторять до тех пор, пока не будет подсчитан весь массив.

Я нашел большой файл json из этого репозитория git https://github.com/zemirco/sf-city-lots-json (~190 МБ).

Файл выглядит следующим образом и я хочу подсчитать количество функций.

{
  "type": "FeatureCollection",
  "features": [ /* lots of feature objects */ ]
}

Вот как выглядит объект feature, если вам интересно.

{
  "type": "Feature",
  "properties": {
    "MAPBLKLOT": "0001001",
    "BLKLOT": "0001001",
    "BLOCK_NUM": "0001",
    "LOT_NUM": "001",
    "FROM_ST": "0",
    "TO_ST": "0",
    "STREET": "UNKNOWN",
    "ST_TYPE": null,
    "ODD_EVEN": "E"
  },
  "geometry": {
    "type": "Polygon",
    "coordinates": [
      [
        [
          -122.422003528252475,
          37.808480096967251,
          0.0
        ],
        [
          -122.422076013325281,
          37.808835019815085,
          0.0
        ],
        [
          -122.421102174348633,
          37.808803534992904,
          0.0
        ],
        [
          -122.421062569067274,
          37.808601056818148,
          0.0
        ],
        [
          -122.422003528252475,
          37.808480096967251,
          0.0
        ]
      ]
    ]
  }
}

Допустим, мое приложение может выделить только 50 МБ, и я пытаюсь загрузить весь файл в память.

Path filePath = Path.of("/src/sf-city-lots-json/citylots.json");
String content = Files.readString(filePath);

Очевидно, что мы не можем загрузить его в память.

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

Gson предоставляет JsonReader , который позволяет считывать поток данных.

public int getFeatureCount(Path filePath) throws Exception {
    int count = 0;
    try (JsonReader reader = new JsonReader(Files.newBufferedReader(filePath))) {
        reader.beginObject();
        while (reader.hasNext()) {
            String name = reader.nextName();
            if ("features".equals(name)) {
                count = getFeatureCountFromArray(reader);
            } else {
                reader.skipValue();
            }
        }
        reader.endObject();
    }
    return count;
}

private int getFeatureCountFromArray(JsonReader reader) throws Exception {
    int count = 0;
    reader.beginArray();
    while (reader.hasNext()) {
        count++;
        reader.beginObject();
        while (reader.hasNext()) {
            reader.skipValue();
        }
        reader.endObject();
    }
    reader.endArray();
    return count;
}

Большая власть приходит с большей ответственностью . В отличие от Gson.FromJSON , нам нужно вызвать begin* , конец* и пропустить значение в нужное время (в соответствии со структурой объекта json), чтобы позволить ему правильно обрабатывать данные, в противном случае он выдаст исключение. Поэтому его следует использовать только тогда, когда у вас есть ограничения по объему памяти или производительности.

Оригинал: “https://dev.to/franzwong/process-large-json-with-limited-memory-12kb”