Достаточно напечатать json, но сохранить внутренние массивы на одной строке python
Я довольно печатаю json в Python, используя этот код:
json.dumps(json_output, indent=2, separators=(',', ': ')
Это печатает мой json как:
{
"rows_parsed": [
[
"a",
"b",
"c",
"d"
],
[
"e",
"f",
"g",
"i"
],
]
}
Однако, я хочу, чтобы он печатался так:
{
"rows_parsed": [
["a","b","c","d"],
["e","f","g","i"],
]
}
Как я могу хранить массивы, которые находятся в массивах, все на одной строке, как указано выше?
Ответы
Ответ 1
Вот способ сделать это с минимальным количеством модификаций:
import json
from json import JSONEncoder
import re
class MarkedList:
_list = None
def __init__(self, l):
self._list = l
z = {
"rows_parsed": [
MarkedList([
"a",
"b",
"c",
"d"
]),
MarkedList([
"e",
"f",
"g",
"i"
]),
]
}
class CustomJSONEncoder(JSONEncoder):
def default(self, o):
if isinstance(o, MarkedList):
return "##<{}>##".format(o._list)
b = json.dumps(z, indent=2, separators=(',', ':'), cls=CustomJSONEncoder)
b = b.replace('"##<', "").replace('>##"', "")
print(b)
В основном, списки, которые вы хотите отформатировать таким образом, вы создаете экземпляр MarkedList
и они анализируются как строки с, как мы надеемся, достаточно уникальной последовательностью, которая впоследствии удаляется из вывода dumps
. Это сделано, чтобы исключить кавычки, заключенные в строку json.
Другой, гораздо более эффективный способ сделать это, но гораздо более уродливый, - это обезьяна исправить патч json.encoder._make_iterencode._iterencode
примерно так:
def _iterencode(o, _current_indent_level):
if isinstance(o, str):
yield _encoder(o)
elif o is None:
yield 'null'
elif o is True:
yield 'true'
elif o is False:
yield 'false'
elif isinstance(o, int):
# see comment for int/float in _make_iterencode
yield _intstr(o)
elif isinstance(o, float):
# see comment for int/float in _make_iterencode
yield _floatstr(o)
elif isinstance(o, MarkedList):
yield _my_custom_parsing(o)
elif isinstance(o, (list, tuple)):
yield from _iterencode_list(o, _current_indent_level)
elif isinstance(o, dict):
yield from _iterencode_dict(o, _current_indent_level)
else:
if markers is not None:
markerid = id(o)
if markerid in markers:
raise ValueError("Circular reference detected")
markers[markerid] = o
o = _default(o)
yield from _iterencode(o, _current_indent_level)
if markers is not None:
del markers[markerid]
Ответ 2
Начиная с решения @martin-gergov, это должно быть немного более надежно:
import json
from json import JSONEncoder
class SingleLine:
def __init__(self, v):
self.wrapped = v
class SingleLineEncoder(JSONEncoder):
def iterencode(self, o, _one_shot=False):
try:
self.wrap(o)
yield from super().iterencode(o, _one_shot=_one_shot)
finally:
# Important: we altered the data structure. Now we must restore it.
self.unwrap(o)
def wrap(self, o):
# Go recursively through all containers (dict, list) in o
if isinstance(o, list):
for i, v in enumerate(o):
if self.must_be_formatted_as_single_line(v):
o[i] = SingleLine(v)
else:
self.wrap(v)
elif isinstance(o, dict):
for k, v in o.items():
if self.must_be_formatted_as_single_line(v):
o[k] = SingleLine(v)
else:
self.wrap(v)
def unwrap(self, o):
# Go recursively through all containers (dict, list) in o
if isinstance(o, list):
for i, v in enumerate(o):
if isinstance(v, SingleLine):
o[i] = v.wrapped
else:
self.unwrap(v)
elif isinstance(o, dict):
for k, v in o.items():
if isinstance(v, SingleLine):
o[k] = v.wrapped
else:
self.unwrap(v)
def must_be_formatted_as_single_line(self, o):
# This is your rule for deciding what to format as a single line
return isinstance(o, list) and all(isinstance(v, str) for v in o)
def default(self, o):
if isinstance(o, SingleLine):
return str(o.wrapped)
return json.JSONEncoder.default(self, o)
z = {
"rows_parsed": [
[
"a",
"b",
"c",
"d"
],
[
"e",
"f",
"g",
"i"
],
]
}
b = json.dumps(z, indent=2, separators=(',', ':'), cls=SingleLineEncoder)
print(b)
Это работает, оборачивая списки, которые вы хотите отобразить в одну строку перед кодированием, и распаковывая, чтобы восстановить состояние. Хитрость заключается в том, чтобы определить хорошее правило для того, что должно быть в одной строке, в must_be_formatted_as_single_line
.
Вывод вышеуказанного кода:
{
"rows_parsed":[
"['a', 'b', 'c', 'd']",
"['e', 'f', 'g', 'i']"
]
}
Производительность снижается, но я не ожидаю, что вы это заметите. В настоящее время это решение не поддерживает кортежи в одной строке - его можно адаптировать для этого, но это будет влиять на производительность в дальнейшем, так как в этом подходе кортежи должны быть заменены списками.
Ответ 3
Я не вижу, как вы могли бы сделать это в json.dumps. После небольшого поиска я наткнулся на несколько вариантов: Один из вариантов - выполнить некоторую постобработку с пользовательской функцией:
def fix_json_indent(text, indent=3):
space_indent = indent * 4
initial = " " * space_indent
json_output = []
current_level_elems = []
all_entries_at_level = None # holder for consecutive entries at exact space_indent level
for line in text.splitlines():
if line.startswith(initial):
if line[space_indent] == " ":
# line indented further than the level
if all_entries_at_level:
current_level_elems.append(all_entries_at_level)
all_entries_at_level = None
item = line.strip()
current_level_elems.append(item)
if item.endswith(","):
current_level_elems.append(" ")
elif current_level_elems:
# line on the same space_indent level
# no more sublevel_entries
current_level_elems.append(line.strip())
json_output.append("".join(current_level_elems))
current_level_elems = []
else:
# line at the exact space_indent level but no items indented further
if all_entries_at_level:
# last pending item was not the start of a new sublevel_entries.
json_output.append(all_entries_at_level)
all_entries_at_level = line.rstrip()
else:
if all_entries_at_level:
json_output.append(all_entries_at_level)
all_entries_at_level = None
if current_level_elems:
json_output.append("".join(current_level_elems))
json_output.append(line)
return "\n".join(json_output)
Другая возможность - регулярное выражение, но оно довольно уродливо и зависит от структуры кода, который вы разместили:
def fix_json_indent(text):
import re
return re.sub('{"', '{\n"', re.sub('\[\[', '[\n[', re.sub('\]\]', ']\n]', re.sub('}', '\n}', text))))
Ответ 4
Имеется модуль npm, который может быть переведен на python (или вызван извне).
https://www.npmjs.com/package/json-stringify-pretty-compact