Иногда нам нужно обработать большой файл 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”