Last updated: Apr 13, 2024
Reading timeยท3 min
The Python "ValueError: Circular reference detected" occurs when you try to convert a dictionary that has a circular reference to a JSON string.
To solve the error, create a deep copy of the dictionary when adding the
circular reference or remove the circular reference before calling
json.dumps()
.
Here is an example of how the error occurs.
import json a_dict = {} a_dict['site'] = 'bobbyhadz.com' # ๐๏ธ this adds a circular reference a_dict['nested'] = a_dict # โ๏ธ ValueError: Circular reference detected json_string = json.dumps(a_dict) print(json_string)
Notice that we added a circular reference to the dictionary before calling json.dumps.
A circular reference is a dictionary key that points to the dictionary itself.
You can verify that the key points to a circular reference by using the is
operator.
a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = a_dict # {'site': 'bobbyhadz.com', 'nested': {...}} print(a_dict) print(a_dict['nested'] is a_dict) # ๐๏ธ True
You cannot convert a dictionary that has a circular reference to a JSON string as this causes the error.
dict.copy()
method to solve the errorOne way to solve the error is to use the dict.copy() method when adding the circular key to the dictionary.
import json a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = a_dict.copy() json_string = json.dumps(a_dict) # ๐๏ธ {"site": "bobbyhadz.com", "nested": {"site": "bobbyhadz.com"}} print(json_string)
The dict.copy()
method returns a shallow copy of the dictionary.
The dictionary is no longer stored in the same location in memory, so we no longer have circular references.
If you want to create a deep copy instead of a shallow copy, use the copy.deepcopy() method.
import json import copy a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = copy.deepcopy(a_dict) json_string = json.dumps(a_dict) print(json_string)
As indicated in the method name, it creates a deep copy of the dictionary, so none of the nested references point to keys in the original dictionary.
json.dumps()
You can also remove the circular reference before calling json.dumps()
method
to solve the error.
import json a_dict = {} a_dict['site'] = 'bobbyhadz.com' a_dict['nested'] = a_dict del a_dict['nested'] json_string = json.dumps(a_dict) print(json_string)
We used the
del
statement to remove the nested
key from the dictionary.
This is the only key that points to a circular reference, so passing the
modified dictionary to json.dumps()
succeeds.
If you need a more robust approach for removing the circular references from an object, use the function from this stackoverflow post.
import json def remove_circular_refs(ob, _seen=None): if _seen is None: _seen = set() if id(ob) in _seen: return None _seen.add(id(ob)) res = ob if isinstance(ob, dict): res = { remove_circular_refs(key, _seen): remove_circular_refs(value, _seen) for key, value in ob.items()} elif isinstance(ob, (list, tuple, set, frozenset)): res = type(ob)(remove_circular_refs(v, _seen) for v in ob) _seen.remove(id(ob)) return res a_dict = {} a_dict['site'] = 'bobbyhadz.com' # ๐๏ธ this adds a circular reference a_dict['nested'] = a_dict json_str = json.dumps(remove_circular_refs(a_dict)) # {"site": "bobbyhadz.com", "nested": null} print(json_str)
The remove_circular_refs
function can be used to remove circular references
from dictionaries, lists, tuples, set
and frozenset
objects.
default
argument of json.dumps()
incorrectlyYou might also get the error if you use the default
argument of json.dumps()
incorrectly.
Here is an example of how to correctly use the default
argument.
import json class Employee(): def __init__(self, data): self.data = data emp1 = Employee({'site': 'bobbyhadz.com'}) def has_data_attribute(obj): if hasattr(obj, 'data'): return obj.data else: raise TypeError(f'Object must have a site attribute, but got {obj}') json_str = json.dumps(emp1, default=has_data_attribute) # ๐๏ธ {"site": "bobbyhadz.com"} print(json_str)
The default
argument must be set to a function that only gets called if the
supplied to json.dumps()
object cannot be serialized with the default
behavior.
The function must return a value that can be serialized by json.dumps()
or
must raise a TypeError
exception.
You might get the "Circular reference detected" error if instead of raising a
TypeError
, your function returns an object that cannot be serialized by the
JSON encoder.
You can learn more about the related topics by checking out the following tutorials: