将输出序列化为 JSON - ValueError:检测到循环引用

Posted

技术标签:

【中文标题】将输出序列化为 JSON - ValueError:检测到循环引用【英文标题】:Serializing output to JSON - ValueError: Circular reference detected 【发布时间】:2012-12-24 07:41:34 【问题描述】:

我正在尝试将我的 mysql 查询结果输出到 JSON。 我在序列化 datetime.datetime 字段时遇到问题,所以我写了一个小函数来做到这一点:

def date_handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    else:
        return obj

然后在我刚刚运行的主代码中:

products_json = []
for code in best_matching_codes:
    cur = db.cursor()
    query = "SELECT * FROM %s WHERE code LIKE '%s'" % (PRODUCTS_TABLE_NAME, product_code)
    cur.execute(query)
    columns = [desc[0] for desc in cur.description]
    rows = cur.fetchall()
    for row in rows:
        products_json.append(dict((k,v) for k,v in zip(columns,row)))   

return json.dumps(products_json, default = date_handler)

但是,由于我编写了 date_handler 函数,我收到“ValueError: Circular reference detected”

127.0.0.1 - - [10/Jan/2013 00:42:18] "GET /1/product?code=9571%2F702 HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1701, in __call__
return self.wsgi_app(environ, start_response)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1689, in wsgi_app
response = self.make_response(self.handle_exception(e))
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1687, in wsgi_app
response = self.full_dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1360, in full_dispatch_request
rv = self.handle_user_exception(e)
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1358, in full_dispatch_request
rv = self.dispatch_request()
  File "/Library/Python/2.7/site-packages/flask/app.py", line 1344, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/pisarzp/Desktop/SusyChoosy/susyAPI/test1.py", line 69, in product_search
return json.dumps(products_json, default = date_handler)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.py", line 238, in dumps
**kw).encode(obj)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 201, in encode
chunks = self.iterencode(o, _one_shot=True)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/encoder.py", line 264, in iterencode
return _iterencode(o, 0)
ValueError: Circular reference detected

我弄坏了什么?有没有更好的方法将输出序列化为 JSON?

【问题讨论】:

你能得到 products_json 的打印输出吗?我猜在日期值的键之间有一些重复。然后它可以得到类似的错误。 【参考方案1】:

您作为default 参数传递的函数只会被json 模块本身不能序列化的对象调用。它必须返回一个可序列化的对象,或者引发一个 TypeError。

如果它不是您正在修复的一种类型(日期),您的版本将返回与您传递的相同对象。这会导致循环引用错误(这是一种误导,因为在经过date_handler 处理后,该圆位于一个对象与其自身之间)。

您可以通过更改date_handler 以在其else 块中引发异常来开始解决此问题。这仍然可能会失败,但您可能可以使用如下代码找出导致问题的数据结构中的对象是什么:

def date_handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    else:
        raise TypeError(
            "Unserializable object  of type ".format(obj, type(obj))
        )

【讨论】:

【参考方案2】:

您应该将调用转发给JSONEncoder 的默认方法,而不是自己提高TypeError

def date_handler(obj):
    if hasattr(obj, 'isoformat'):
        return obj.isoformat()
    else:
        json.JSONEncoder.default(self,obj)

这也会引发TypeError,这是一种更好的做法,它允许JSONEncoder 尝试编码您的方法无法编码的类型。

【讨论】:

我收到了NameError: global name 'self' is not defined 尝试返回 str(obj) 我在 json 中的数组列表遇到了同样的问题,然后更改变量名,然后错误消失了【参考方案3】:
json.dumps(obj, default=method_name)

“method_name”函数必须返回一个序列化对象。

def method_name(obj):
    data = 
            '__class__': obj.__class__.__name__,
            '__module__': obj.__module__
           
    data.update(obj.__dict__)
    return data

【讨论】:

以上是关于将输出序列化为 JSON - ValueError:检测到循环引用的主要内容,如果未能解决你的问题,请参考以下文章

python 将SQLAlchemy Model序列化为字典(用于JSON输出)并从字典属性更新Model。

使用 datagridview 将列表序列化为现有的 .Json 文件

Symfony - 将 json 反序列化为实体数组

将对象列表序列化为没有父对象名称的 JSON

使用 select_related 将查询中的数据结果序列化为 json

在 Spring Boot 中自动将 Rest 控制器序列化为 CSV 而不是 JSON