From 42fd42ddbeac7a49fe89a3f09dd6f59fbb1a1e55 Mon Sep 17 00:00:00 2001 From: Sep Dehpour Date: Sat, 14 Dec 2024 23:51:55 -0800 Subject: [PATCH] fixes to_json() method chokes on some standard json.dumps() such as sort_keys #490 --- deepdiff/serialization.py | 30 ++++++++++++++++++++++++++---- tests/test_serialization.py | 8 ++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/deepdiff/serialization.py b/deepdiff/serialization.py index e350b3c..1ad12a5 100644 --- a/deepdiff/serialization.py +++ b/deepdiff/serialization.py @@ -179,7 +179,7 @@ def from_json_pickle(cls, value): else: logger.error('jsonpickle library needs to be installed in order to run from_json_pickle') # pragma: no cover. Json pickle is getting deprecated. - def to_json(self, default_mapping=None, **kwargs): + def to_json(self, default_mapping: dict | None=None, force_use_builtin_json=False, **kwargs): """ Dump json of the text view. **Parameters** @@ -190,6 +190,11 @@ def to_json(self, default_mapping=None, **kwargs): If you have a certain object type that the json serializer can not serialize it, please pass the appropriate type conversion through this dictionary. + force_use_builtin_json: Boolean, default = False + When True, we use Python's builtin Json library for serialization, + even if Orjson is installed. + + kwargs: Any other kwargs you pass will be passed on to Python's json.dumps() **Example** @@ -212,7 +217,12 @@ def to_json(self, default_mapping=None, **kwargs): '{"type_changes": {"root": {"old_type": "A", "new_type": "B", "old_value": "obj A", "new_value": "obj B"}}}' """ dic = self.to_dict(view_override=TEXT_VIEW) - return json_dumps(dic, default_mapping=default_mapping, **kwargs) + return json_dumps( + dic, + default_mapping=default_mapping, + force_use_builtin_json=force_use_builtin_json, + **kwargs, + ) def to_dict(self, view_override=None): """ @@ -637,14 +647,26 @@ def object_hook(self, obj): return obj -def json_dumps(item, default_mapping=None, **kwargs): +def json_dumps(item, default_mapping=None, force_use_builtin_json: bool=False, **kwargs): """ Dump json with extra details that are not normally json serializable + + parameters + ---------- + + force_use_builtin_json: Boolean, default = False + When True, we use Python's builtin Json library for serialization, + even if Orjson is installed. """ - if orjson: + if orjson and not force_use_builtin_json: indent = kwargs.pop('indent', None) if indent: kwargs['option'] = orjson.OPT_INDENT_2 + if 'sort_keys' in kwargs: + raise TypeError( + "orjson does not accept the sort_keys parameter. " + "If you need to pass sort_keys, set force_use_builtin_json=True " + "to use Python's built-in json library instead of orjson.") return orjson.dumps( item, default=json_convertor_default(default_mapping=default_mapping), diff --git a/tests/test_serialization.py b/tests/test_serialization.py index d578e53..3c50683 100644 --- a/tests/test_serialization.py +++ b/tests/test_serialization.py @@ -47,6 +47,14 @@ def test_serialization_text(self): jsoned = ddiff.to_json() assert "world" in jsoned + def test_serialization_text_force_builtin_json(self): + ddiff = DeepDiff(t1, t2) + with pytest.raises(TypeError) as excinfo: + jsoned = ddiff.to_json(sort_keys=True) + assert str(excinfo.value).startswith("orjson does not accept the sort_keys parameter.") + jsoned = ddiff.to_json(sort_keys=True, force_use_builtin_json=True) + assert "world" in jsoned + def test_deserialization(self): ddiff = DeepDiff(t1, t2) jsoned = ddiff.to_json_pickle()