Skip to content

gh-91114: Added argument for json dump/dumps to print arrays (from lists and tuples) on one line #31762

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions Doc/library/json.rst
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,12 @@ Basic Usage
.. versionchanged:: 3.6
All optional parameters are now :ref:`keyword-only <keyword-only_parameter>`.

:param bool arr_oneline:
**New in this release.**
If ``True``, JSON arrays (Python lists and tuples) will be serialized
on a single line regardless of the specified *indent* level. This allows
for a more compact representation of array data while still pretty-printing
objects. Default is ``False``.

.. function:: dumps(obj, *, skipkeys=False, ensure_ascii=True, \
check_circular=True, allow_nan=True, cls=None, \
Expand Down Expand Up @@ -450,8 +456,7 @@ Encoders and Decoders
This can be used to decode a JSON document from a string that may have
extraneous data at the end.


.. class:: JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None)
.. class:: JSONEncoder(*, skipkeys=False, ensure_ascii=True, check_circular=True, allow_nan=True, sort_keys=False, indent=None, separators=None, default=None, arr_oneline=False)

Extensible JSON encoder for Python data structures.

Expand Down Expand Up @@ -518,19 +523,26 @@ Encoders and Decoders
.. versionchanged:: 3.2
Allow strings for *indent* in addition to integers.

If specified, *separators* should be an ``(item_separator, key_separator)``
tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and
``(',', ': ')`` otherwise. To get the most compact JSON representation,
If specified, *separators* should be a ``(item_separator, key_separator)``
tuple. The default is ``(', ', ': ')`` if *indent* is ``None``
and ``(',', ': ')`` otherwise. To get the most compact JSON representation,
you should specify ``(',', ':')`` to eliminate whitespace.

.. versionchanged:: 3.4
Use ``(',', ': ')`` as default if *indent* is not ``None``.

If specified, *default* should be a function that gets called for objects that
can't otherwise be serialized. It should return a JSON encodable version of
the object or raise a :exc:`TypeError`. If not specified, :exc:`TypeError`
is raised.


:param bool arr_oneline:
**New in this release.**
When ``True``, JSON arrays (lists and tuples) are rendered on a single line,
even if an *indent* level is specified. This option allows for a more compact
representation of array data while still enabling pretty-printing of objects.
Default is ``False``.

.. versionchanged:: 3.6
All parameters are now :ref:`keyword-only <keyword-only_parameter>`.

Expand Down Expand Up @@ -828,4 +840,4 @@ Command-line options
<https://www.rfc-editor.org/errata_search.php?rfc=7159>`_,
JSON permits literal U+2028 (LINE SEPARATOR) and
U+2029 (PARAGRAPH SEPARATOR) characters in strings, whereas JavaScript
(as of ECMAScript Edition 5.1) does not.
(as of ECMAScript Edition 5.1) does not.
14 changes: 9 additions & 5 deletions Lib/json/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@

def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw):
default=None, sort_keys=False, arr_oneline=False, **kw):
"""Serialize ``obj`` as a JSON formatted stream to ``fp`` (a
``.write()``-supporting file-like object).

Expand Down Expand Up @@ -155,6 +155,8 @@ def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,

If *sort_keys* is true (default: ``False``), then the output of
dictionaries will be sorted by key.
If *arr_oneline* is true (default: ``False``), then lists/tuples will be
encoded as arrays on a single line.

To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with
Expand All @@ -172,8 +174,8 @@ def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,
cls = JSONEncoder
iterable = cls(skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators,
default=default, sort_keys=sort_keys, **kw).iterencode(obj)
separators=separators, default=default, sort_keys=sort_keys,
arr_oneline=arr_oneline, **kw).iterencode(obj)
# could accelerate with writelines in some versions of Python, at
# a debuggability cost
for chunk in iterable:
Expand All @@ -182,7 +184,7 @@ def dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True,

def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
allow_nan=True, cls=None, indent=None, separators=None,
default=None, sort_keys=False, **kw):
default=None, sort_keys=False, arr_oneline=False, **kw):
"""Serialize ``obj`` to a JSON formatted ``str``.

If ``skipkeys`` is true then ``dict`` keys that are not basic types
Expand Down Expand Up @@ -217,6 +219,8 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,

If *sort_keys* is true (default: ``False``), then the output of
dictionaries will be sorted by key.
If *arr_oneline* is true (default: ``False``), then lists/tuples will be
encoded as arrays on a single line.

To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the
``.default()`` method to serialize additional types), specify it with
Expand All @@ -235,7 +239,7 @@ def dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
skipkeys=skipkeys, ensure_ascii=ensure_ascii,
check_circular=check_circular, allow_nan=allow_nan, indent=indent,
separators=separators, default=default, sort_keys=sort_keys,
**kw).encode(obj)
arr_oneline=arr_oneline, **kw).encode(obj)


_default_decoder = JSONDecoder(object_hook=None, object_pairs_hook=None)
Expand Down
18 changes: 14 additions & 4 deletions Lib/json/encoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class JSONEncoder(object):
key_separator = ': '
def __init__(self, *, skipkeys=False, ensure_ascii=True,
check_circular=True, allow_nan=True, sort_keys=False,
indent=None, separators=None, default=None):
indent=None, separators=None, default=None, arr_oneline=False):
"""Constructor for JSONEncoder, with sensible defaults.

If skipkeys is false, then it is a TypeError to attempt
Expand All @@ -129,6 +129,10 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True,
sorted by key; this is useful for regression tests to ensure
that JSON serializations can be compared on a day-to-day basis.

If arr_oneline is true, then lists/tuples will be output as arrays
on a single line. If false, a newline character will be placed after each
array element.

If indent is a non-negative integer, then JSON array
elements and object members will be pretty-printed with that
indent level. An indent level of 0 will only insert newlines.
Expand All @@ -151,6 +155,7 @@ def __init__(self, *, skipkeys=False, ensure_ascii=True,
self.allow_nan = allow_nan
self.sort_keys = sort_keys
self.indent = indent
self.arr_oneline = arr_oneline
if separators is not None:
self.item_separator, self.key_separator = separators
elif indent is not None:
Expand Down Expand Up @@ -257,11 +262,12 @@ def floatstr(o, allow_nan=self.allow_nan,
_iterencode = _make_iterencode(
markers, self.default, _encoder, indent, floatstr,
self.key_separator, self.item_separator, self.sort_keys,
self.skipkeys, _one_shot)
self.skipkeys, _one_shot, self.arr_oneline)
return _iterencode(o, 0)

def _make_iterencode(markers, _default, _encoder, _indent, _floatstr,
_key_separator, _item_separator, _sort_keys, _skipkeys, _one_shot,
arr_oneline,
## HACK: hand-optimized bytecode; turn globals into locals
ValueError=ValueError,
dict=dict,
Expand All @@ -287,7 +293,10 @@ def _iterencode_list(lst, _current_indent_level):
buf = '['
if _indent is not None:
_current_indent_level += 1
newline_indent = '\n' + _indent * _current_indent_level
if not arr_oneline:
newline_indent = '\n' + _indent * _current_indent_level
else:
newline_indent = ''
separator = _item_separator + newline_indent
buf += newline_indent
else:
Expand Down Expand Up @@ -329,7 +338,8 @@ def _iterencode_list(lst, _current_indent_level):
raise
if newline_indent is not None:
_current_indent_level -= 1
yield '\n' + _indent * _current_indent_level
if not arr_oneline:
yield '\n' + _indent * _current_indent_level
yield ']'
if markers is not None:
del markers[markerid]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Added optional argument *list_oneline* to :meth:`json.dump` and
:meth:`json.dumps` for printing array output on a single
line. Patch by Daniel Marshall.
Loading