Какие есть способы уменьшения размера файла с JSON сгенерированного скриптом?
Eсть скрипт, который генерирует JSON нужного мне формата.
Пример сгенерированного файла:
{
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"area": 1
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[90.00526999999931, -0.004729999999999964],
[90.00553999999863, -0.004729999999999964],
[90.00526999999931, -0.004459999999999928],
[90.00553999999863, -0.004459999999999928]
]
]
}
},{
"type": "Feature",
"properties": {
"area": 81
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[90.00742999999386, -0.002569999999999677],
[90.00769999999319, -0.002569999999999677],
[90.00742999999386, -0.0022999999999996413],
[90.00769999999319, -0.0022999999999996413]
]
]
}
}]
}
Проблема в том, что файл, который генерируется скриптом очень большой. Я сгенерировал меньшие файлы, и посчитал, что полный файл у меня займет примеро 0.5Tb, что очень уж много.
Можно ли как-то с помощью средств Python уменьшить размер файла?
Например, мне достаточно 7 знаков после запятой в каждом элементе типа [90.00742999999386, -0.002569999999999677], что по логике должно уменьшить размер файла.
Есть еще какие-то способы и средства для уменьшения размера файла?
UPDATE: доп. информация из коментариев:
JSON нужен для того, чтобы строить области по GEO-координатам. В каждом 'area' есть 4 координаты (4 точки, по которым строим область). Далее, этот файл я использую в Tableau для отображения на карте.
Ответы (3 шт):
Tableau умеет читать данные из многих источников, поэтому стоит рассмотреть возможность сохранять ваши геоданные в одну из бесплатных БД, поддерживающих хранение геоданных, например: MongoDB, PostgreSQL, Vertica, etc.
Если вы решите продолжить использование JSON, то вот несколько советов:
- обратите внимание на ограничение: "A single JSON object cannot exceed 128 MB"
сохраняйте JSON файл используя следующие параметры (это самый компактный способ - все содержимое JSN файла будет сохранено в одной строке и без пробелов):
json.dum(..., indent=None, separators=(",", ":"), ...)`
Я проверил внутреннюю реализацию метода, ответсвенного за представление float объектов в JSON и был неприятно удивлен - код устроен очень негибко и не нашел простого и эффективного способа изменить представление float чисел в JSON.
Поэтому решил написать собственный класс:
from json.encoder import _make_iterencode, c_make_encoder
from json.encoder import *
FLOATSTR = "{:.7f}".format
class CompactEncoder(json.JSONEncoder):
def __init__(self, *args, **kwargs):
kwargs["separators"] = (",", ":")
super().__init__(*args, **kwargs)
def encode(self, obj):
if isinstance(obj, float):
return FLOATSTR(obj)
return json.JSONEncoder.encode(self, obj)
def iterencode(self, o, _one_shot=False):
if self.check_circular:
markers = {}
else:
markers = None
if self.ensure_ascii:
_encoder = encode_basestring_ascii
else:
_encoder = encode_basestring
def floatstr(o, allow_nan=self.allow_nan,
_repr=FLOATSTR, _inf=INFINITY, _neginf=-INFINITY):
if o != o:
text = 'NaN'
elif o == _inf:
text = 'Infinity'
elif o == _neginf:
text = '-Infinity'
else:
return _repr(o)
if not allow_nan:
raise ValueError(
"Out of range float values are not JSON compliant: " +
repr(o))
return text
if (_one_shot and c_make_encoder is not None
and self.indent is None):
_iterencode = c_make_encoder(
markers, self.default, _encoder, self.indent,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, self.allow_nan)
else:
_iterencode = _make_iterencode(
markers, self.default, _encoder, self.indent, floatstr,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, _one_shot)
return _iterencode(o, 0)
Примеры использования:
In [22]: with open(r"c:/temp/normal.json", "w") as f:
...: json.dump(d, f, indent=4)
In [23]: with open(r"c:/temp/compact.json", "w") as f:
...: json.dump(d, f, cls=CompactEncoder)
Сравнение размеров получившихся JSON файлов:
In [25]: from pathlib import Path
In [26]: Path(r"c:/temp/normal.json").stat().st_size
Out[26]: 1904
In [27]: Path(r"c:/temp/compact.json").stat().st_size
Out[27]: 416
In [28]: Path(r"c:/temp/compact.json").stat().st_size / Path(r"c:/temp/normal.json").stat().st_size
Out[28]: 0.2184873949579832
Я бы предложил воспользоваться offline утилиткой jtc (погуглите jtc и json).
Табуляция и отступы в JSON нужны только лишь для удобства чтения. Для программных обработчиков пробелы между ключами и значениями абсолютно излишни. Можно хранить JSON совершенно без них, плотно сжав JSON без пробелов и перносов строк.
Так, ваш пример занимает вот столько символов (предположим он находится в файле file.json):
bash $ <file.json cat | wc -c
1035
bash $
Убрав все пробелы он сократится почти вдвое:
bash $ <file.json jtc -r -t0 | wc -c
563
bash $
Ну и конечно же дальнейшее сжатие можно произвести обрубив все цифоровые значения до 7 знаков после точки:
bash $ <file.json jtc -w'<^-?\d+\.\d{1,7}>D:' -u0 -T'{$0}' -rt0 | wc -c
417
bash $
Кстати, если нужно внести изменения сразу в исходный файл, то можно воспользоваться ключиком -f:
bash $ jtc -w'<^-?\d+\.\d{1,7}>D:' -u0 -T'{$0}' -rt0 -f file.json
bash $ cat file.json | wc -c
417
bash $