Python 3, чтение/запись сжатых json-объектов из/в файл gzip

Для Python3 я выполнил @Martijn Pieters code с помощью этого:

import gzip
import json

# writing
with gzip.GzipFile(jsonfilename, 'w') as fout:
    for i in range(N):
        uid = "whatever%i" % i
        dv = [1, 2, 3]
        data = json.dumps({
            'what': uid,
            'where': dv})

        fout.write(data + '\n')

но это приводит к ошибке:

Traceback (most recent call last):
    ...
  File "C:\Users\Think\my_json.py", line 118, in write_json
    fout.write(data + '\n')
  File "C:\Users\Think\Anaconda3\lib\gzip.py", line 258, in write
    data = memoryview(data)
TypeError: memoryview: a bytes-like object is required, not 'str'

Любые мысли о том, что происходит?

Ответ 1

Здесь есть четыре этапа трансформации.

  • структура данных Python (вложенные диктофоны, списки, строки, числа, булевы)
  • строка Python, содержащая сериализованное представление этой структуры данных ( "JSON" )
  • список байтов, содержащий представление этой строки ( "UTF-8" )
  • список байтов, содержащий представление этого предыдущего списка байтов ( "gzip" )

Итак, сделайте эти шаги один за другим.

import gzip
import json

data = []
for i in range(N):
    uid = "whatever%i" % i
    dv = [1, 2, 3]
    data.append({
        'what': uid,
        'where': dv
    })                                           # 1. data

json_str = json.dumps(data) + "\n"               # 2. string (i.e. JSON)
json_bytes = json_str.encode('utf-8')            # 3. bytes (i.e. UTF-8)

with gzip.GzipFile(jsonfilename, 'w') as fout:   # 4. gzip
    fout.write(json_bytes)                       

Обратите внимание, что добавление "\n" здесь совершенно лишнее. Он ничего не нарушает, но помимо этого он бесполезен.

Чтение работает точно так же:

with gzip.GzipFile(jsonfilename, 'r') as fin:    # 4. gzip
    json_bytes = fin.read()                      # 3. bytes (i.e. UTF-8)

json_str = json_bytes.decode('utf-8')            # 2. string (i.e. JSON)
data = json.loads(json_str)                      # 1. data

print(data)

Конечно, шаги можно объединить:

with gzip.GzipFile(jsonfilename, 'w') as fout:
    fout.write(json.dumps(data).encode('utf-8'))                       

и

with gzip.GzipFile(jsonfilename, 'r') as fin:
    data = json.loads(fin.read().decode('utf-8'))

Ответ 2

Решение, упомянутое в fooobar.com/info/15408367/... (спасибо, @Rafe), имеет большое преимущество: поскольку кодирование выполняется на лету, вы не создаете два полных промежуточных строковых объекта сгенерированного json. С большими объектами это экономит память.

В дополнение к упомянутому посту, расшифровка также проста:

with gzip.open(filename, 'rt', encoding='ascii') as zipfile:
    my_object = json.load(zipfile)