Используйте Python для управления форматом json.dump() с помощью kwarg "default="?

В Python мне приходится сохранять в файл хранилища массив пользовательских данных, состоящий из объектов JSON (простой формат: [{}, {}...]). По мере того, как программа собирает и записывает пользовательские данные, массив [] потенциально увеличивается до нескольких тысяч объектов. Каждый объект {} состоит примерно из пятидесяти пар ключ_значение. Как мне написать специальную функцию для json.dump(), используя собственный kwarg "default=", который будет начинать каждый объект с новой строки и добавлять между ними дополнительное строковое пространство? Использование kwarg "indent=" для красивой печати массива делает файл слишком длинным по вертикали, и без этого kwarg я получаю плотную массу трудночитаемых объектов.

Чтобы проиллюстрировать проблему, возьмем гораздо более короткий массив. example = [{"a":1, "b":2, "c":3,}, {"d":1, "e":2, "f":3,}, {"g":1, "h":2, "i":3}, {"j":6, "k":7, "l":8}] Без использования отступов вывод в мой файл .json будет слишком компактным.

[{"a": 1, "b": 2, "c": 3}, {"d": 1, "e": 2, "f": 3}, {"g": 1, "h": 2, "i": 3}, {"j": 6, "k": 7, "l": 8}]

Если я использую отступ, я получаю узкий вертикальный столбец.

[
  {
    "a": 1,
    "b": 2,
    "c": 3
  },
  {
    "d": 1,
    "e": 2,
    "f": 3
  },
  {
    "g": 1,
    "h": 2,
    "i": 3
  },
  {
    "j": 6,
    "k": 7,
    "l": 8
  }
]

Чтобы свести к минимуму пробелы, сохраняя при этом читабельность, я хочу, чтобы вывод был в этом формате (здесь он организован вручную).

[
    {"a": 1, "b": 2, "c": 3},

    {"d": 1, "e": 2, "f": 3},

    {"g": 1, "h": 2, "i": 3}, 

    {"j": 6, "k": 7, "l": 8}
]

Когда каждый объект содержит до пятидесяти пар ключ:значение, этот формат, даже с переносом, достигает обоих целей. Я удивлен, что метод не предоставляет такой возможности. Но я не могу найти достаточно информации об основах кода JSON, чтобы вводить символы новой строки и дополнительные пробелы в существующем методе json.dump(). Любая помощь?

Почему вы удивлены? Ничего подобного нет ни в JavaScript, ни в PHP. Как и Python, они предоставляют выбор только между одной строкой и всем, что находится в новой строке. Детальный контроль форматирования невозможен.

Barmar 27.06.2024 00:09

Если ваш объект верхнего уровня представляет собой массив, рассмотрите вместо этого формат строк JSON (JSONL). Это буквально один объект JSON на строку без [] снаружи. Преимущество состоит в том, что вы можете легко добавлять данные в JSONL, не переписывая весь файл, что очень повышает эффективность. Читается тоже легко: просто for line in file: json.loads(line).

nneonneo 27.06.2024 01:58

Если вы пытаетесь минимизировать пробелы, почему между каждым словарем в выводе появляется новая строка? Это, конечно, не функциональное требование.

SIGHUP 27.06.2024 10:39

@SIGHUP: Поскольку в моей реальной программе словари гораздо длиннее, дополнительные строчные пробелы составляют относительно небольшую часть общего пространства — и это того стоит для улучшения читаемости.

prainbow 27.06.2024 23:34

Все ответы подчеркивают точку зрения Бармара и diggusbickus о том, что то, что я хотел сделать, не может быть сделано изначально, а должно быть достигнуто с помощью обходного пути. Я протестировал фрагмент пользователя user24714692, и он сработал отлично, создав файл JSON, который мог загрузить json.load(). SIGHUP я не тестировал, так как начинаю со словарей без отступов и мне приходится записывать их в файл. Часть сегодняшнего дня я провел за чтением документации по JSON Lines. В конце концов, преимущество возможности каждый раз добавлять, а не перезаписывать весь файл данных, привлекло меня к решению @nneonneo.

prainbow 27.06.2024 23:55
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
5
71
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Вы можете использовать модуль json с dumps():

import json


def _dump_one(example, p):
    with open(p, 'w') as f:
        f.write('[\n')
        for i, e in enumerate(example):
            s = json.dumps(e)
            f.write(f'\t{s}')
            if i < len(example) - 1:
                f.write(',\n\n')
            else:
                f.write('\n')
        f.write(']\n')


example = [
    {"a": 1, "b": 2, "c": 3},
    {"d": 1, "e": 2, "f": 3},
    {"g": 1, "h": 2, "i": 3},
    {"j": 6, "k": 7, "l": 8}
]

_dump_one(example, 'file.json')


Вы также можете write() это сделать только один раз:

import json


def _dump_two(example, p):
    res = '[\n'
    for i, e in enumerate(example):
        s = json.dumps(e)
        res += f'\t{s}'
        if i < len(example) - 1:
            res += ',\n\n'
        else:
            res += '\n'
    res += ']\n'

    with open(p, 'w') as f:
        f.write(res)


example = [
    {"a": 1, "b": 2, "c": 3},
    {"d": 1, "e": 2, "f": 3},
    {"g": 1, "h": 2, "i": 3},
    {"j": 6, "k": 7, "l": 8}
]

_dump_two(example, 'file.json')

Ответ принят как подходящий

Если ваш объект верхнего уровня представляет собой массив, рассмотрите вместо этого формат JSON Lines (JSONL). Это буквально один объект JSON на строку без [] снаружи. Преимущество заключается в том, что вы можете легко добавлять данные в JSONL, не переписывая весь файл, что очень важно для эффективности: вызов dump для создания дампа постоянно расширяющегося массива приведет к линейному замедлению каждого дампа (общее квадратичное время!), тогда как добавление к строкам JSON файл имеет постоянное время (общее линейное время для всех записей).

Чтобы добавить новые объекты в файл строк JSON:

with open("output.jsonl", "a") as outf:
    json.dump(new_record, outf)
    outf.write("\n")

Чтобы прочитать все записи из файла JSON Lines:

with open("output.jsonl", "r") as inf:
    for row in inf:
        obj = json.loads(row)
        # then process obj or append it to a list, etc.

Очень полезное предложение. Однако в моей ситуации я обнаружил, что мне приходится добавлять новые объекты, используя собственный специальный синтаксис JSON Lines, открываясь с помощью «with jsonlines.open(file, mode='a') aswriter:», а затем записывая с помощью «writer.write». (новый_obj)». После записи нового объекта в файл автоматически создается новая пронумерованная строка с курсором, готовая к приему другого объекта. JSON.dump() просто добавляет новые объекты к существующей строке — не совсем тот результат, который я надеялся получить.

prainbow 29.06.2024 23:59

Как мне написать специальную функцию для json.dump(), используя собственный kwarg "default=", который будет начинать каждый объект с новой строки и добавлять между ними дополнительное строковое пространство?

ты не можешь. default будет применяться только к значениям, а не к «строкам», и даже тогда ваши значения сериализуемы.

Если указано, по умолчанию должна быть функция, которая вызывается для объектов, которые иначе нельзя сериализовать — Документация модуля json

Вы можете использовать json.dumps() для форматирования отдельных словарей и добавления форматированного вывода в список. Впоследствии вы можете использовать str.join(), чтобы получить требуемый формат вывода.

import json

a = [
  {
    "a": 1,
    "b": 2,
    "c": 3
  },
  {
    "d": 1,
    "e": 2,
    "f": 3
  },
  {
    "g": 1,
    "h": 2,
    "i": 3
  },
  {
    "j": 6,
    "k": 7,
    "l": 8
  }
]

d = [json.dumps(_d) for _d in a]

print("[\n\t" + ",\n\n\t".join(d) + "\n]")

Выход:

[
        {"a": 1, "b": 2, "c": 3},

        {"d": 1, "e": 2, "f": 3},

        {"g": 1, "h": 2, "i": 3},

        {"j": 6, "k": 7, "l": 8}
]

Другие вопросы по теме