对 Python 返回 JSON 作为字符串而不是文字感到困惑

Posted

技术标签:

【中文标题】对 Python 返回 JSON 作为字符串而不是文字感到困惑【英文标题】:Confused by Python returning JSON as string instead of literal 【发布时间】:2013-11-12 02:35:29 【问题描述】:

我在 RoR 和 Rails 中完成了一些编码,当我通过 API 调用返回 JSON 对象时,它返回为

"id" : "1", "name" : "Dan"

但是在 Python(使用 Flask 和 Flask-SQLAlchemy)中,当我通过 json.dumps 或 jsonpickle.encode 返回 JSON 对象时,它会返回为

" \"id\" : \"1\", \"name\": \"Dan\" " 看起来非常笨拙,因为它在另一端不容易被解析(在这种情况下由 ios 应用程序 - Obj-C)。

我在这里遗漏了什么,我应该怎么做才能将它作为 JSON 文字而不是 JSON 字符串返回?

这是我的代码的样子:

people = models.UserRelationships.query.filter_by(user_id=user_id, active=ACTIVE_RECORD)
friends = people.filter_by(friends=YES)

json_object = jsonpickle.encode(friends.first().as_dict(), unpicklable=False, keys=True)
print(json_object)  # this prints here, i.e.  "id" : "1", "name" : "Dan" 

return json_object # this returns " \"id\" : \"1\", \"name\": \"Dan\" " to the browser

【问题讨论】:

json.dumps 将返回对象的字符串表示形式。请参阅帮助(json.dumps)-> 将 obj 序列化为 JSON 格式的 str 如果你想要JSON,你应该使用相关模块序列化为JSON。打印一些东西不会把它变成 JSON。 一个 JSON 文字 is 一个字符串。 JSON 是一种表示法。 JSON 只能是一个字符序列。您将数据结构与其表示混淆了。 你显示的代码不可能做你声称的事情。 json_object = print(json_object) 会将 json_object 设置为 None 是的,你是对的。我写代码太快了!对不起。我在上面编辑过。问题是当我将它返回到浏览器时,而不是当我打印它时。 【参考方案1】:

您的理解中缺少的是,当您在 Python 中使用 JSON 模块时,您不是在使用 JSON 对象。 JSON 根据定义只是一个符合特定标准的字符串。

假设你有字符串:

friends = '"name": "Fred", "id": 1'

如果你想在 python 中处理这些数据,你需要将它加载到一个 python 对象中:

import json
friends_obj = json.loads(friends)

此时friends_obj是一个python字典。

如果你想转换它(或任何其他 python 字典或列表),那么这就是 json.dumps 派上用场的地方:

friends_str = json.dumps(friends_obj)
print friends_str
'"name": "Fred", "id": 1'

但是,如果我们尝试“转储”原始朋友字符串,您会看到不同的结果:

dumped_str = json.dumps(friends)
print dumped_str
'"\\"name\\": \\"Fred\\", \\"id\\": 1"'

这是因为您基本上是在尝试将普通字符串编码为 JSON,并且它正在转义字符。我希望这有助于理解事情!

干杯

【讨论】:

谢谢,问题不在于打印。打印时没问题,当我将它返回给浏览器时,它是双重编码的。即return json_object # this returns " \"id\" : \"1\", \"name\": \"Dan\" " 到浏览器【参考方案2】:

看起来您在这里使用 Django,在这种情况下,请执行类似的操作

from django.utils import simplejson as json
...
return HttpResponse(json.dumps(friends.first().as_dict()))

【讨论】:

我没有使用Django,所以我想我不能使用HttpResponse?我正在使用 Python、Flask、Flask-SQLAlchemy.. 啊,对不起,我草率下结论;一些语法看起来像 Django 但不是。 Django 是模块化的,因此您可以根据需要使用一些部分,但如果您使用的是 Flask,则可能不需要。【参考方案3】:

这几乎总是表明您在某处对数据进行了双重编码。例如:

>>> obj =  "id" : "1", "name" : "Dan" 
>>> j = json.dumps(obj)
>>> jj = json.dumps(j)
>>> print(obj)
'id': '1', 'name': 'Dan'
>>> print(j)
"id": "1", "name": "Dan"
>>> print(jj)
"\"id\": \"1\", \"name\": \"Dan\""

这里,jj 是一个完全有效的 JSON 字符串表示形式——但它不是 obj 的表示形式,它是字符串 j 的表示形式,这是没有用的。

通常你不会直接这样做;相反,要么你从一个 JSON 字符串而不是一个对象开始(例如,你从客户端请求或文本文件中获取它),要么你调用了库中的某个函数,如 requestsjsonpickle使用已编码的字符串隐式调用json.dumps。但无论哪种方式,都是同一个问题,但解决方案相同:只是不要双重编码。

【讨论】:

我在 Flask-SQLAlchemy 行上使用 jsonpickle.encode,该行已转换为 dict(即jsonpickle.encode(friends.first().as_dict(), unpicklable=False, keys=True)),因此除非我误解了某些内容,否则它可能还不是编码字符串。 @Dan:你从什么地方退回来的?您的应用程序是如何配置的?如果 Flask 期望你给它一个它将编码的对象,而你正在编码一个对象并给它结果字符串,它最终会被双重编码。 @Dan:顺便说一句,你为什么使用jsonpickle 而不是stdlib json 模块,或者Flask 的内置jsonifyjsonpickle 的全部意义在于,它为您提供了一种包装和解包不直接映射到 JSON 的 Python 类型(如自定义类)的方法,除非对方也使用 jsonpickle,否则这是无用的,这是网络浏览器或 ObjC 应用显然不是。 你是对的。那是我的问题:/我应该一直在使用 jsonify。【参考方案4】:

您应该使用flask.jsonify,它不仅会正确编码,还会相应地设置content-type 标头。

people = models.UserRelationships.query.filter_by(user_id=user_id, active=ACTIVE_RECORD)
friends = people.filter_by(friends=YES)

return jsonify(friends.first().as_dict())

【讨论】:

谢谢!我的问题是无法理解 http 响应和 JSON 字符串之间的区别。将它用于 API 时,我正在关注 http 响应。

以上是关于对 Python 返回 JSON 作为字符串而不是文字感到困惑的主要内容,如果未能解决你的问题,请参考以下文章