diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index bad06329c4bfa..c45959d475af4 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -749,6 +749,7 @@ I/O - Bug in :meth:`read_stata` where the missing code for double was not recognised for format versions 105 and prior (:issue:`58149`) - Bug in :meth:`set_option` where setting the pandas option ``display.html.use_mathjax`` to ``False`` has no effect (:issue:`59884`) - Bug in :meth:`to_excel` where :class:`MultiIndex` columns would be merged to a single row when ``merge_cells=False`` is passed (:issue:`60274`) +- Bug in :meth:`to_json` raising ``OverflowError`` when convert DataFrame.dtypes Series to JSON (:issue:`61170`) Period ^^^^^^ diff --git a/pandas/_libs/src/vendored/ujson/python/objToJSON.c b/pandas/_libs/src/vendored/ujson/python/objToJSON.c index 8342dbcd1763d..2ab695e9ea02e 100644 --- a/pandas/_libs/src/vendored/ujson/python/objToJSON.c +++ b/pandas/_libs/src/vendored/ujson/python/objToJSON.c @@ -62,6 +62,7 @@ int object_is_series_type(PyObject *obj); int object_is_index_type(PyObject *obj); int object_is_nat_type(PyObject *obj); int object_is_na_type(PyObject *obj); +int object_is_ndtypes_type(PyObject *obj); typedef struct __NpyArrContext { PyObject *array; @@ -396,6 +397,25 @@ static const char *PyDecimalToUTF8Callback(JSOBJ _obj, JSONTypeContext *tc, return outValue; } +static const char *PyNpyDtypesToUTF8Callback(JSOBJ _obj, JSONTypeContext *tc, + size_t *len) { + PyObject *obj = (PyObject *)_obj; + PyObject *str = PyObject_Str(obj); + + if (str == NULL) { + ((JSONObjectEncoder *)tc->encoder)->errorMsg = ""; + return NULL; + } + + GET_TC(tc)->newObj = str; + + Py_ssize_t s_len; + char *outValue = (char *)PyUnicode_AsUTF8AndSize(str, &s_len); + *len = s_len; + + return outValue; +} + //============================================================================= // Numpy array iteration functions //============================================================================= @@ -1583,6 +1603,10 @@ static void Object_beginTypeContext(JSOBJ _obj, JSONTypeContext *tc) { } else if (object_is_na_type(obj)) { tc->type = JT_NULL; return; + } else if (object_is_ndtypes_type(obj)) { + tc->type = JT_UTF8; + pc->PyTypeToUTF8 = PyNpyDtypesToUTF8Callback; + return; } ISITERABLE: diff --git a/pandas/_libs/src/vendored/ujson/python/ujson.c b/pandas/_libs/src/vendored/ujson/python/ujson.c index 2ee084b9304f4..d590e51f1fa44 100644 --- a/pandas/_libs/src/vendored/ujson/python/ujson.c +++ b/pandas/_libs/src/vendored/ujson/python/ujson.c @@ -211,6 +211,16 @@ int object_is_na_type(PyObject *obj) { } return result; } + +int object_is_ndtypes_type(PyObject *obj) { + PyObject *ndtype = (PyObject *)&PyArrayDescr_Type; + int result = PyObject_IsInstance(obj, ndtype); + if (result == -1) { + PyErr_Clear(); + return 0; + } + return result; +} #else /* Used in objToJSON.c */ int object_is_decimal_type(PyObject *obj) { diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py index 32eeb30de4b69..00cebd0a2248f 100644 --- a/pandas/tests/io/json/test_pandas.py +++ b/pandas/tests/io/json/test_pandas.py @@ -2311,3 +2311,21 @@ def test_large_number(): ) expected = Series([9999999999999999]) tm.assert_series_equal(result, expected) + + +@pytest.mark.parametrize( + "df", + [ + DataFrame({"A": [1, 2, 3], "B": [4, 5, 6]}), + DataFrame({"X": [1.1, 2.2], "Y": ["a", "b"]}), + ], +) +def test_dtypes_to_json_consistency(df: DataFrame): + # GH 61170 + expected = df.dtypes.apply(str).to_json() + result = df.dtypes.to_json() + result = json.loads(result) + for k in result: + if "name" in result[k]: + result[k] = result[k]["name"] + assert result == json.loads(expected)