Skip to content

Commit c94eaee

Browse files
authoredSep 18, 2019
Add Indent Support in to_json (#28130)
1 parent 0a082d4 commit c94eaee

File tree

9 files changed

+326
-78
lines changed

9 files changed

+326
-78
lines changed
 

‎doc/source/whatsnew/v1.0.0.rst

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Other enhancements
3636
when using the ``pyarrow`` engine. It is currently not yet supported when converting back to
3737
pandas (so it will become an integer or float dtype depending on the presence of missing data).
3838
(:issue:`28368`)
39-
-
39+
- :meth:`DataFrame.to_json` now accepts an ``indent`` integer argument to enable pretty printing of JSON output (:issue:`12004`)
4040

4141

4242
Build Changes
@@ -217,6 +217,7 @@ I/O
217217
- Improve infinity parsing. :meth:`read_csv` now interprets ``Infinity``, ``+Infinity``, ``-Infinity`` as floating point values (:issue:`10065`)
218218
- Bug in :meth:`DataFrame.to_csv` where values were truncated when the length of ``na_rep`` was shorter than the text input data. (:issue:`25099`)
219219
- Bug in :func:`DataFrame.to_string` where values were truncated using display options instead of outputting the full content (:issue:`9784`)
220+
- Bug in :meth:`DataFrame.to_json` where a datetime column label would not be written out in ISO format with ``orient="table"`` (:issue:`28130`)
220221

221222
Plotting
222223
^^^^^^^^

‎pandas/_libs/src/ujson/lib/ultrajson.h

+4
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,10 @@ typedef struct __JSONObjectEncoder {
244244
If true, '<', '>', and '&' characters will be encoded as \u003c, \u003e, and \u0026, respectively. If false, no special encoding will be used. */
245245
int encodeHTMLChars;
246246

247+
/*
248+
Configuration for spaces of indent */
249+
int indent;
250+
247251
/*
248252
Set to an error message if error occurred */
249253
const char *errorMsg;

‎pandas/_libs/src/ujson/lib/ultrajsonenc.c

+26-2
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,22 @@ FASTCALL_ATTR INLINE_PREFIX void FASTCALL_MSVC strreverse(char *begin,
728728
while (end > begin) aux = *end, *end-- = *begin, *begin++ = aux;
729729
}
730730

731+
void Buffer_AppendIndentNewlineUnchecked(JSONObjectEncoder *enc)
732+
{
733+
if (enc->indent > 0) Buffer_AppendCharUnchecked(enc, '\n');
734+
}
735+
736+
// This function could be refactored to only accept enc as an argument,
737+
// but this is a straight vendor from ujson source
738+
void Buffer_AppendIndentUnchecked(JSONObjectEncoder *enc, JSINT32 value)
739+
{
740+
int i;
741+
if (enc->indent > 0)
742+
while (value-- > 0)
743+
for (i = 0; i < enc->indent; i++)
744+
Buffer_AppendCharUnchecked(enc, ' ');
745+
}
746+
731747
void Buffer_AppendIntUnchecked(JSONObjectEncoder *enc, JSINT32 value) {
732748
char *wstr;
733749
JSUINT32 uvalue = (value < 0) ? -value : value;
@@ -960,24 +976,28 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name,
960976
enc->iterBegin(obj, &tc);
961977

962978
Buffer_AppendCharUnchecked(enc, '[');
979+
Buffer_AppendIndentNewlineUnchecked (enc);
963980

964981
while (enc->iterNext(obj, &tc)) {
965982
if (count > 0) {
966983
Buffer_AppendCharUnchecked(enc, ',');
967984
#ifndef JSON_NO_EXTRA_WHITESPACE
968985
Buffer_AppendCharUnchecked(buffer, ' ');
969986
#endif
987+
Buffer_AppendIndentNewlineUnchecked (enc);
970988
}
971989

972990
iterObj = enc->iterGetValue(obj, &tc);
973991

974992
enc->level++;
993+
Buffer_AppendIndentUnchecked (enc, enc->level);
975994
encode(iterObj, enc, NULL, 0);
976995
count++;
977996
}
978997

979998
enc->iterEnd(obj, &tc);
980-
Buffer_Reserve(enc, 2);
999+
Buffer_AppendIndentNewlineUnchecked (enc);
1000+
Buffer_AppendIndentUnchecked (enc, enc->level);
9811001
Buffer_AppendCharUnchecked(enc, ']');
9821002
break;
9831003
}
@@ -987,25 +1007,29 @@ void encode(JSOBJ obj, JSONObjectEncoder *enc, const char *name,
9871007
enc->iterBegin(obj, &tc);
9881008

9891009
Buffer_AppendCharUnchecked(enc, '{');
1010+
Buffer_AppendIndentNewlineUnchecked (enc);
9901011

9911012
while (enc->iterNext(obj, &tc)) {
9921013
if (count > 0) {
9931014
Buffer_AppendCharUnchecked(enc, ',');
9941015
#ifndef JSON_NO_EXTRA_WHITESPACE
9951016
Buffer_AppendCharUnchecked(enc, ' ');
9961017
#endif
1018+
Buffer_AppendIndentNewlineUnchecked (enc);
9971019
}
9981020

9991021
iterObj = enc->iterGetValue(obj, &tc);
10001022
objName = enc->iterGetName(obj, &tc, &szlen);
10011023

10021024
enc->level++;
1025+
Buffer_AppendIndentUnchecked (enc, enc->level);
10031026
encode(iterObj, enc, objName, szlen);
10041027
count++;
10051028
}
10061029

10071030
enc->iterEnd(obj, &tc);
1008-
Buffer_Reserve(enc, 2);
1031+
Buffer_AppendIndentNewlineUnchecked (enc);
1032+
Buffer_AppendIndentUnchecked (enc, enc->level);
10091033
Buffer_AppendCharUnchecked(enc, '}');
10101034
break;
10111035
}

‎pandas/_libs/src/ujson/python/objToJSON.c

+17-7
Original file line numberDiff line numberDiff line change
@@ -2373,10 +2373,16 @@ char *Object_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen) {
23732373
}
23742374

23752375
PyObject *objToJSON(PyObject *self, PyObject *args, PyObject *kwargs) {
2376-
static char *kwlist[] = {
2377-
"obj", "ensure_ascii", "double_precision", "encode_html_chars",
2378-
"orient", "date_unit", "iso_dates", "default_handler",
2379-
NULL};
2376+
static char *kwlist[] = {"obj",
2377+
"ensure_ascii",
2378+
"double_precision",
2379+
"encode_html_chars",
2380+
"orient",
2381+
"date_unit",
2382+
"iso_dates",
2383+
"default_handler",
2384+
"indent",
2385+
NULL};
23802386

23812387
char buffer[65536];
23822388
char *ret;
@@ -2389,6 +2395,7 @@ PyObject *objToJSON(PyObject *self, PyObject *args, PyObject *kwargs) {
23892395
char *sdateFormat = NULL;
23902396
PyObject *oisoDates = 0;
23912397
PyObject *odefHandler = 0;
2398+
int indent = 0;
23922399

23932400
PyObjectEncoder pyEncoder = {{
23942401
Object_beginTypeContext,
@@ -2410,6 +2417,7 @@ PyObject *objToJSON(PyObject *self, PyObject *args, PyObject *kwargs) {
24102417
idoublePrecision,
24112418
1, // forceAscii
24122419
0, // encodeHTMLChars
2420+
0, // indent
24132421
}};
24142422
JSONObjectEncoder *encoder = (JSONObjectEncoder *)&pyEncoder;
24152423

@@ -2434,10 +2442,10 @@ PyObject *objToJSON(PyObject *self, PyObject *args, PyObject *kwargs) {
24342442

24352443
PRINTMARK();
24362444

2437-
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OiOssOO", kwlist, &oinput,
2438-
&oensureAscii, &idoublePrecision,
2445+
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OiOssOOi", kwlist,
2446+
&oinput, &oensureAscii, &idoublePrecision,
24392447
&oencodeHTMLChars, &sOrient, &sdateFormat,
2440-
&oisoDates, &odefHandler)) {
2448+
&oisoDates, &odefHandler, &indent)) {
24412449
return NULL;
24422450
}
24432451

@@ -2503,6 +2511,8 @@ PyObject *objToJSON(PyObject *self, PyObject *args, PyObject *kwargs) {
25032511
pyEncoder.defaultHandler = odefHandler;
25042512
}
25052513

2514+
encoder->indent = indent;
2515+
25062516
pyEncoder.originalOutputFormat = pyEncoder.outputFormat;
25072517
PRINTMARK();
25082518
ret = JSON_EncodeObject(oinput, encoder, buffer, sizeof(buffer));

‎pandas/_typing.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
FilePathOrBuffer = Union[str, Path, IO[AnyStr]]
2323

2424
FrameOrSeries = TypeVar("FrameOrSeries", bound="NDFrame")
25-
Scalar = Union[str, int, float]
25+
Scalar = Union[str, int, float, bool]
2626
Axis = Union[str, int]
2727
Ordered = Optional[bool]
2828

‎pandas/core/generic.py

+31-12
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import re
99
from textwrap import dedent
1010
from typing import (
11+
Any,
1112
Callable,
1213
Dict,
1314
FrozenSet,
@@ -60,7 +61,7 @@
6061
from pandas.core.dtypes.missing import isna, notna
6162

6263
import pandas as pd
63-
from pandas._typing import Dtype, FilePathOrBuffer
64+
from pandas._typing import Dtype, FilePathOrBuffer, Scalar
6465
from pandas.core import missing, nanops
6566
import pandas.core.algorithms as algos
6667
from pandas.core.base import PandasObject, SelectionMixin
@@ -2245,17 +2246,18 @@ def to_excel(
22452246

22462247
def to_json(
22472248
self,
2248-
path_or_buf=None,
2249-
orient=None,
2250-
date_format=None,
2251-
double_precision=10,
2252-
force_ascii=True,
2253-
date_unit="ms",
2254-
default_handler=None,
2255-
lines=False,
2256-
compression="infer",
2257-
index=True,
2258-
):
2249+
path_or_buf: Optional[FilePathOrBuffer] = None,
2250+
orient: Optional[str] = None,
2251+
date_format: Optional[str] = None,
2252+
double_precision: int = 10,
2253+
force_ascii: bool_t = True,
2254+
date_unit: str = "ms",
2255+
default_handler: Optional[Callable[[Any], Union[Scalar, List, Dict]]] = None,
2256+
lines: bool_t = False,
2257+
compression: Optional[str] = "infer",
2258+
index: bool_t = True,
2259+
indent: Optional[int] = None,
2260+
) -> Optional[str]:
22592261
"""
22602262
Convert the object to a JSON string.
22612263
@@ -2335,6 +2337,11 @@ def to_json(
23352337
23362338
.. versionadded:: 0.23.0
23372339
2340+
indent : integer, optional
2341+
Length of whitespace used to indent each record.
2342+
2343+
.. versionadded:: 1.0.0
2344+
23382345
Returns
23392346
-------
23402347
None or str
@@ -2345,6 +2352,13 @@ def to_json(
23452352
--------
23462353
read_json
23472354
2355+
Notes
2356+
-----
2357+
The behavior of ``indent=0`` varies from the stdlib, which does not
2358+
indent the output but does insert newlines. Currently, ``indent=0``
2359+
and the default ``indent=None`` are equivalent in pandas, though this
2360+
may change in a future release.
2361+
23482362
Examples
23492363
--------
23502364
@@ -2395,6 +2409,10 @@ def to_json(
23952409
date_format = "iso"
23962410
elif date_format is None:
23972411
date_format = "epoch"
2412+
2413+
config.is_nonnegative_int(indent)
2414+
indent = indent or 0
2415+
23982416
return json.to_json(
23992417
path_or_buf=path_or_buf,
24002418
obj=self,
@@ -2407,6 +2425,7 @@ def to_json(
24072425
lines=lines,
24082426
compression=compression,
24092427
index=index,
2428+
indent=indent,
24102429
)
24112430

24122431
def to_hdf(self, path_or_buf, key, **kwargs):

‎pandas/io/json/_json.py

+68-48
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from collections import OrderedDict
12
from io import StringIO
23
from itertools import islice
34
import os
5+
from typing import Any, Callable, Dict, List, Optional, Type, Union
46

57
import numpy as np
68

@@ -11,6 +13,7 @@
1113
from pandas.core.dtypes.common import ensure_str, is_period_dtype
1214

1315
from pandas import DataFrame, MultiIndex, Series, isna, to_datetime
16+
from pandas._typing import Scalar
1417
from pandas.core.reshape.concat import concat
1518

1619
from pandas.io.common import (
@@ -31,20 +34,23 @@
3134

3235
TABLE_SCHEMA_VERSION = "0.20.0"
3336

37+
Serializable = Union[Scalar, List, Dict]
38+
3439

3540
# interface to/from
3641
def to_json(
3742
path_or_buf,
3843
obj,
39-
orient=None,
40-
date_format="epoch",
41-
double_precision=10,
42-
force_ascii=True,
43-
date_unit="ms",
44-
default_handler=None,
45-
lines=False,
46-
compression="infer",
47-
index=True,
44+
orient: Optional[str] = None,
45+
date_format: str = "epoch",
46+
double_precision: int = 10,
47+
force_ascii: bool = True,
48+
date_unit: str = "ms",
49+
default_handler: Optional[Callable[[Any], Serializable]] = None,
50+
lines: bool = False,
51+
compression: Optional[str] = "infer",
52+
index: bool = True,
53+
indent: int = 0,
4854
):
4955

5056
if not index and orient not in ["split", "table"]:
@@ -59,7 +65,7 @@ def to_json(
5965
if orient == "table" and isinstance(obj, Series):
6066
obj = obj.to_frame(name=obj.name or "values")
6167
if orient == "table" and isinstance(obj, DataFrame):
62-
writer = JSONTableWriter
68+
writer = JSONTableWriter # type: Type["Writer"]
6369
elif isinstance(obj, Series):
6470
writer = SeriesWriter
6571
elif isinstance(obj, DataFrame):
@@ -76,6 +82,7 @@ def to_json(
7682
date_unit=date_unit,
7783
default_handler=default_handler,
7884
index=index,
85+
indent=indent,
7986
).write()
8087

8188
if lines:
@@ -97,18 +104,19 @@ class Writer:
97104
def __init__(
98105
self,
99106
obj,
100-
orient,
101-
date_format,
102-
double_precision,
103-
ensure_ascii,
104-
date_unit,
105-
index,
106-
default_handler=None,
107+
orient: Optional[str],
108+
date_format: str,
109+
double_precision: int,
110+
ensure_ascii: bool,
111+
date_unit: str,
112+
index: bool,
113+
default_handler: Optional[Callable[[Any], Serializable]] = None,
114+
indent: int = 0,
107115
):
108116
self.obj = obj
109117

110118
if orient is None:
111-
orient = self._default_orient
119+
orient = self._default_orient # type: ignore
112120

113121
self.orient = orient
114122
self.date_format = date_format
@@ -117,6 +125,7 @@ def __init__(
117125
self.date_unit = date_unit
118126
self.default_handler = default_handler
119127
self.index = index
128+
self.indent = indent
120129

121130
self.is_copy = None
122131
self._format_axes()
@@ -133,17 +142,19 @@ def write(self):
133142
self.date_unit,
134143
self.date_format == "iso",
135144
self.default_handler,
145+
self.indent,
136146
)
137147

138148
def _write(
139149
self,
140150
obj,
141-
orient,
142-
double_precision,
143-
ensure_ascii,
144-
date_unit,
145-
iso_dates,
146-
default_handler,
151+
orient: Optional[str],
152+
double_precision: int,
153+
ensure_ascii: bool,
154+
date_unit: str,
155+
iso_dates: bool,
156+
default_handler: Optional[Callable[[Any], Serializable]],
157+
indent: int,
147158
):
148159
return dumps(
149160
obj,
@@ -153,6 +164,7 @@ def _write(
153164
date_unit=date_unit,
154165
iso_dates=iso_dates,
155166
default_handler=default_handler,
167+
indent=indent,
156168
)
157169

158170

@@ -169,12 +181,13 @@ def _format_axes(self):
169181
def _write(
170182
self,
171183
obj,
172-
orient,
173-
double_precision,
174-
ensure_ascii,
175-
date_unit,
176-
iso_dates,
177-
default_handler,
184+
orient: Optional[str],
185+
double_precision: int,
186+
ensure_ascii: bool,
187+
date_unit: str,
188+
iso_dates: bool,
189+
default_handler: Optional[Callable[[Any], Serializable]],
190+
indent: int,
178191
):
179192
if not self.index and orient == "split":
180193
obj = {"name": obj.name, "data": obj.values}
@@ -186,6 +199,7 @@ def _write(
186199
date_unit,
187200
iso_dates,
188201
default_handler,
202+
indent,
189203
)
190204

191205

@@ -214,12 +228,13 @@ def _format_axes(self):
214228
def _write(
215229
self,
216230
obj,
217-
orient,
218-
double_precision,
219-
ensure_ascii,
220-
date_unit,
221-
iso_dates,
222-
default_handler,
231+
orient: Optional[str],
232+
double_precision: int,
233+
ensure_ascii: bool,
234+
date_unit: str,
235+
iso_dates: bool,
236+
default_handler: Optional[Callable[[Any], Serializable]],
237+
indent: int,
223238
):
224239
if not self.index and orient == "split":
225240
obj = obj.to_dict(orient="split")
@@ -232,6 +247,7 @@ def _write(
232247
date_unit,
233248
iso_dates,
234249
default_handler,
250+
indent,
235251
)
236252

237253

@@ -241,20 +257,22 @@ class JSONTableWriter(FrameWriter):
241257
def __init__(
242258
self,
243259
obj,
244-
orient,
245-
date_format,
246-
double_precision,
247-
ensure_ascii,
248-
date_unit,
249-
index,
250-
default_handler=None,
260+
orient: Optional[str],
261+
date_format: str,
262+
double_precision: int,
263+
ensure_ascii: bool,
264+
date_unit: str,
265+
index: bool,
266+
default_handler: Optional[Callable[[Any], Serializable]] = None,
267+
indent: int = 0,
251268
):
252269
"""
253270
Adds a `schema` attribute with the Table Schema, resets
254271
the index (can't do in caller, because the schema inference needs
255272
to know what the index is, forces orient to records, and forces
256273
date_format to 'iso'.
257274
"""
275+
258276
super().__init__(
259277
obj,
260278
orient,
@@ -264,6 +282,7 @@ def __init__(
264282
date_unit,
265283
index,
266284
default_handler=default_handler,
285+
indent=indent,
267286
)
268287

269288
if date_format != "iso":
@@ -315,19 +334,20 @@ def _write(
315334
date_unit,
316335
iso_dates,
317336
default_handler,
337+
indent,
318338
):
319-
data = super()._write(
320-
obj,
339+
table_obj = OrderedDict((("schema", self.schema), ("data", obj)))
340+
serialized = super()._write(
341+
table_obj,
321342
orient,
322343
double_precision,
323344
ensure_ascii,
324345
date_unit,
325346
iso_dates,
326347
default_handler,
348+
indent,
327349
)
328-
serialized = '{{"schema": {schema}, "data": {data}}}'.format(
329-
schema=dumps(self.schema), data=data
330-
)
350+
331351
return serialized
332352

333353

‎pandas/tests/io/json/test_json_table_schema.py

+24-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import numpy as np
66
import pytest
77

8+
from pandas.compat import PY35
9+
810
from pandas.core.dtypes.dtypes import CategoricalDtype, DatetimeTZDtype, PeriodDtype
911

1012
import pandas as pd
@@ -20,6 +22,14 @@
2022
)
2123

2224

25+
def assert_results_equal(result, expected):
26+
"""Helper function for comparing deserialized JSON with Py35 compat."""
27+
if PY35:
28+
assert sorted(result.items()) == sorted(expected.items())
29+
else:
30+
assert result == expected
31+
32+
2333
class TestBuildSchema:
2434
def setup_method(self, method):
2535
self.df = DataFrame(
@@ -234,7 +244,8 @@ def test_build_series(self):
234244
),
235245
]
236246
)
237-
assert result == expected
247+
248+
assert_results_equal(result, expected)
238249

239250
def test_to_json(self):
240251
df = self.df.copy()
@@ -323,7 +334,8 @@ def test_to_json(self):
323334
),
324335
]
325336
expected = OrderedDict([("schema", schema), ("data", data)])
326-
assert result == expected
337+
338+
assert_results_equal(result, expected)
327339

328340
def test_to_json_float_index(self):
329341
data = pd.Series(1, index=[1.0, 2.0])
@@ -352,7 +364,8 @@ def test_to_json_float_index(self):
352364
),
353365
]
354366
)
355-
assert result == expected
367+
368+
assert_results_equal(result, expected)
356369

357370
def test_to_json_period_index(self):
358371
idx = pd.period_range("2016", freq="Q-JAN", periods=2)
@@ -372,7 +385,8 @@ def test_to_json_period_index(self):
372385
OrderedDict([("index", "2016-02-01T00:00:00.000Z"), ("values", 1)]),
373386
]
374387
expected = OrderedDict([("schema", schema), ("data", data)])
375-
assert result == expected
388+
389+
assert_results_equal(result, expected)
376390

377391
def test_to_json_categorical_index(self):
378392
data = pd.Series(1, pd.CategoricalIndex(["a", "b"]))
@@ -406,7 +420,8 @@ def test_to_json_categorical_index(self):
406420
),
407421
]
408422
)
409-
assert result == expected
423+
424+
assert_results_equal(result, expected)
410425

411426
def test_date_format_raises(self):
412427
with pytest.raises(ValueError):
@@ -542,7 +557,8 @@ def test_categorical(self):
542557
),
543558
]
544559
)
545-
assert result == expected
560+
561+
assert_results_equal(result, expected)
546562

547563
@pytest.mark.parametrize(
548564
"idx,nm,prop",
@@ -596,7 +612,8 @@ def test_timestamp_in_columns(self):
596612
)
597613
result = df.to_json(orient="table")
598614
js = json.loads(result)
599-
assert js["schema"]["fields"][1]["name"] == 1451606400000
615+
assert js["schema"]["fields"][1]["name"] == "2016-01-01T00:00:00.000Z"
616+
# TODO - below expectation is not correct; see GH 28256
600617
assert js["schema"]["fields"][2]["name"] == 10000
601618

602619
@pytest.mark.parametrize(

‎pandas/tests/io/json/test_pandas.py

+153
Original file line numberDiff line numberDiff line change
@@ -1610,3 +1610,156 @@ def test_tuple_labels(self, orient, expected):
16101610
df = pd.DataFrame([[1]], index=[("a", "b")], columns=[("c", "d")])
16111611
result = df.to_json(orient=orient)
16121612
assert result == expected
1613+
1614+
@pytest.mark.parametrize("indent", [1, 2, 4])
1615+
def test_to_json_indent(self, indent):
1616+
# GH 12004
1617+
df = pd.DataFrame([["foo", "bar"], ["baz", "qux"]], columns=["a", "b"])
1618+
1619+
result = df.to_json(indent=indent)
1620+
spaces = " " * indent
1621+
expected = """{{
1622+
{spaces}"a":{{
1623+
{spaces}{spaces}"0":"foo",
1624+
{spaces}{spaces}"1":"baz"
1625+
{spaces}}},
1626+
{spaces}"b":{{
1627+
{spaces}{spaces}"0":"bar",
1628+
{spaces}{spaces}"1":"qux"
1629+
{spaces}}}
1630+
}}""".format(
1631+
spaces=spaces
1632+
)
1633+
1634+
assert result == expected
1635+
1636+
@pytest.mark.parametrize(
1637+
"orient,expected",
1638+
[
1639+
(
1640+
"split",
1641+
"""{
1642+
"columns":[
1643+
"a",
1644+
"b"
1645+
],
1646+
"index":[
1647+
0,
1648+
1
1649+
],
1650+
"data":[
1651+
[
1652+
"foo",
1653+
"bar"
1654+
],
1655+
[
1656+
"baz",
1657+
"qux"
1658+
]
1659+
]
1660+
}""",
1661+
),
1662+
(
1663+
"records",
1664+
"""[
1665+
{
1666+
"a":"foo",
1667+
"b":"bar"
1668+
},
1669+
{
1670+
"a":"baz",
1671+
"b":"qux"
1672+
}
1673+
]""",
1674+
),
1675+
(
1676+
"index",
1677+
"""{
1678+
"0":{
1679+
"a":"foo",
1680+
"b":"bar"
1681+
},
1682+
"1":{
1683+
"a":"baz",
1684+
"b":"qux"
1685+
}
1686+
}""",
1687+
),
1688+
(
1689+
"columns",
1690+
"""{
1691+
"a":{
1692+
"0":"foo",
1693+
"1":"baz"
1694+
},
1695+
"b":{
1696+
"0":"bar",
1697+
"1":"qux"
1698+
}
1699+
}""",
1700+
),
1701+
(
1702+
"values",
1703+
"""[
1704+
[
1705+
"foo",
1706+
"bar"
1707+
],
1708+
[
1709+
"baz",
1710+
"qux"
1711+
]
1712+
]""",
1713+
),
1714+
(
1715+
"table",
1716+
"""{
1717+
"schema":{
1718+
"fields":[
1719+
{
1720+
"name":"index",
1721+
"type":"integer"
1722+
},
1723+
{
1724+
"name":"a",
1725+
"type":"string"
1726+
},
1727+
{
1728+
"name":"b",
1729+
"type":"string"
1730+
}
1731+
],
1732+
"primaryKey":[
1733+
"index"
1734+
],
1735+
"pandas_version":"0.20.0"
1736+
},
1737+
"data":[
1738+
{
1739+
"index":0,
1740+
"a":"foo",
1741+
"b":"bar"
1742+
},
1743+
{
1744+
"index":1,
1745+
"a":"baz",
1746+
"b":"qux"
1747+
}
1748+
]
1749+
}""",
1750+
),
1751+
],
1752+
)
1753+
def test_json_indent_all_orients(self, orient, expected):
1754+
# GH 12004
1755+
df = pd.DataFrame([["foo", "bar"], ["baz", "qux"]], columns=["a", "b"])
1756+
result = df.to_json(orient=orient, indent=4)
1757+
1758+
if PY35:
1759+
assert json.loads(result) == json.loads(expected)
1760+
else:
1761+
assert result == expected
1762+
1763+
def test_json_negative_indent_raises(self):
1764+
with pytest.raises(ValueError, match="must be a nonnegative integer"):
1765+
pd.DataFrame().to_json(indent=-1)

0 commit comments

Comments
 (0)
Please sign in to comment.