Skip to content

Commit e52e37a

Browse files
BUG: Fixed encoding of pd.NA with to_json (pandas-dev#31748)
1 parent 6ed4cd2 commit e52e37a

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

doc/source/whatsnew/v1.0.2.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ Fixed regressions
2525
Bug fixes
2626
~~~~~~~~~
2727

28-
-
29-
-
28+
**I/O**
29+
30+
- Using ``pd.NA`` with :meth:`DataFrame.to_json` now correctly outputs a null value instead of an empty object (:issue:`31615`)
3031

3132
.. ---------------------------------------------------------------------------
3233

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

+12
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ static PyTypeObject *cls_dataframe;
5454
static PyTypeObject *cls_series;
5555
static PyTypeObject *cls_index;
5656
static PyTypeObject *cls_nat;
57+
static PyTypeObject *cls_na;
5758
PyObject *cls_timedelta;
5859

5960
npy_int64 get_nat(void) { return NPY_MIN_INT64; }
@@ -151,6 +152,7 @@ int PdBlock_iterNext(JSOBJ, JSONTypeContext *);
151152
void *initObjToJSON(void) {
152153
PyObject *mod_pandas;
153154
PyObject *mod_nattype;
155+
PyObject *mod_natype;
154156
PyObject *mod_decimal = PyImport_ImportModule("decimal");
155157
type_decimal =
156158
(PyTypeObject *)PyObject_GetAttrString(mod_decimal, "Decimal");
@@ -176,6 +178,12 @@ void *initObjToJSON(void) {
176178
Py_DECREF(mod_nattype);
177179
}
178180

181+
mod_natype = PyImport_ImportModule("pandas._libs.missing");
182+
if (mod_natype) {
183+
cls_na = (PyTypeObject *)PyObject_GetAttrString(mod_natype, "NAType");
184+
Py_DECREF(mod_natype);
185+
}
186+
179187
/* Initialise numpy API */
180188
import_array();
181189
// GH 31463
@@ -1909,6 +1917,10 @@ void Object_beginTypeContext(JSOBJ _obj, JSONTypeContext *tc) {
19091917
"%R (0d array) is not JSON serializable at the moment",
19101918
obj);
19111919
goto INVALID;
1920+
} else if (PyObject_TypeCheck(obj, cls_na)) {
1921+
PRINTMARK();
1922+
tc->type = JT_NULL;
1923+
return;
19121924
}
19131925

19141926
ISITERABLE:

pandas/tests/io/json/test_pandas.py

+41
Original file line numberDiff line numberDiff line change
@@ -1640,3 +1640,44 @@ def test_deprecate_numpy_argument_read_json(self):
16401640
with tm.assert_produces_warning(FutureWarning):
16411641
result = read_json(expected.to_json(), numpy=True)
16421642
tm.assert_frame_equal(result, expected)
1643+
1644+
def test_frame_int_overflow(self):
1645+
# GH 30320
1646+
encoded_json = json.dumps([{"col": "31900441201190696999"}, {"col": "Text"}])
1647+
expected = DataFrame({"col": ["31900441201190696999", "Text"]})
1648+
result = read_json(encoded_json)
1649+
tm.assert_frame_equal(result, expected)
1650+
1651+
@pytest.mark.parametrize(
1652+
"dataframe,expected",
1653+
[
1654+
(
1655+
pd.DataFrame({"x": [1, 2, 3], "y": ["a", "b", "c"]}),
1656+
'{"(0, \'x\')":1,"(0, \'y\')":"a","(1, \'x\')":2,'
1657+
'"(1, \'y\')":"b","(2, \'x\')":3,"(2, \'y\')":"c"}',
1658+
)
1659+
],
1660+
)
1661+
def test_json_multiindex(self, dataframe, expected):
1662+
series = dataframe.stack()
1663+
result = series.to_json(orient="index")
1664+
assert result == expected
1665+
1666+
def test_to_s3(self, s3_resource):
1667+
# GH 28375
1668+
mock_bucket_name, target_file = "pandas-test", "test.json"
1669+
df = DataFrame({"x": [1, 2, 3], "y": [2, 4, 6]})
1670+
df.to_json(f"s3://{mock_bucket_name}/{target_file}")
1671+
assert target_file in (
1672+
obj.key for obj in s3_resource.Bucket("pandas-test").objects.all()
1673+
)
1674+
1675+
def test_json_pandas_na(self):
1676+
# GH 31615
1677+
result = pd.DataFrame([[pd.NA]]).to_json()
1678+
assert result == '{"0":{"0":null}}'
1679+
1680+
def test_json_pandas_nulls(self, nulls_fixture):
1681+
# GH 31615
1682+
result = pd.DataFrame([[nulls_fixture]]).to_json()
1683+
assert result == '{"0":{"0":null}}'

0 commit comments

Comments
 (0)