diff --git a/doc/source/whatsnew/v3.0.0.rst b/doc/source/whatsnew/v3.0.0.rst index 2b437734a451a..a095e78af3bfa 100644 --- a/doc/source/whatsnew/v3.0.0.rst +++ b/doc/source/whatsnew/v3.0.0.rst @@ -750,6 +750,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` period dtype was not being converted to string (:issue:`55490`) Period ^^^^^^ diff --git a/pandas/_libs/tslibs/offsets.pyx b/pandas/_libs/tslibs/offsets.pyx index a16964435ef50..e15c04726741b 100644 --- a/pandas/_libs/tslibs/offsets.pyx +++ b/pandas/_libs/tslibs/offsets.pyx @@ -568,6 +568,19 @@ cdef class BaseOffset: out += ": " + ", ".join(attrs) return out + def toDict(self) -> dict: + """ + Convert BaseOffset object to a dictionary representation + and used for JSON serialization. + """ + d = {} + # Add all attributes defined in _attributes + for attr in self._attributes: + if hasattr(self, attr): + d[attr] = getattr(self, attr) + + return d + @property def name(self) -> str: """ @@ -5108,8 +5121,8 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str: warnings.warn( f"\'{name}\' is deprecated and will be removed " f"in a future version, please use " - f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\'" - f" instead.", + f"\'{c_PERIOD_AND_OFFSET_DEPR_FREQSTR.get(name)}\' " + f"instead.", FutureWarning, stacklevel=find_stack_level(), ) @@ -5122,8 +5135,8 @@ def _warn_about_deprecated_aliases(name: str, is_period: bool) -> str: warnings.warn( f"\'{name}\' is deprecated and will be removed " f"in a future version, please use " - f"\'{_name}\'" - f" instead.", + f"\'{_name}\' " + f"instead.", FutureWarning, stacklevel=find_stack_level(), ) diff --git a/pandas/tests/io/json/test_pandas.py b/pandas/tests/io/json/test_pandas.py index 32eeb30de4b69..227681a2b1fe1 100644 --- a/pandas/tests/io/json/test_pandas.py +++ b/pandas/tests/io/json/test_pandas.py @@ -2079,6 +2079,38 @@ def e(self): series = Series([_TestObject(a=1, b=2, _c=3, d=4)]) assert json.loads(series.to_json()) == {"0": {"a": 1, "b": 2, "d": 4}} + def test_to_json_with_period(self): + # GH55490 + ser = Series(pd.period_range(start=2021, freq="Y", periods=1)) + result = ser.to_json() + expected = ( + '{"0":{' + '"day":31,' + '"day_of_week":4,' + '"day_of_year":365,' + '"dayofweek":4,' + '"dayofyear":365,' + '"days_in_month":31,' + '"daysinmonth":31,' + '"end_time":1640995199999,' + '"freq":{"n":1,"normalize":false,"month":12},' + '"freqstr":"Y-DEC",' + '"hour":0,' + '"is_leap_year":false,' + '"minute":0,' + '"month":12,' + '"ordinal":51,' + '"quarter":4,' + '"qyear":2021,' + '"second":0,' + '"start_time":1609459200000,' + '"week":52,' + '"weekday":4,' + '"weekofyear":52,' + '"year":2021}}' + ) + assert result == expected + @pytest.mark.parametrize( "data,expected", [ diff --git a/pandas/tests/tseries/offsets/test_offsets.py b/pandas/tests/tseries/offsets/test_offsets.py index f5c2c06162fcb..9186fd9cd5315 100644 --- a/pandas/tests/tseries/offsets/test_offsets.py +++ b/pandas/tests/tseries/offsets/test_offsets.py @@ -1227,3 +1227,12 @@ def test_is_yqm_start_end(): def test_multiply_dateoffset_typeerror(left, right): with pytest.raises(TypeError, match="Cannot multiply"): left * right + + +def test_toDict(offset_types): + offset = offset_types(n=2) + d = offset.toDict() + + for attr in offset._attributes: + if hasattr(offset, attr): + assert d[attr] == getattr(offset, attr)