为什么在解析JSON时json.loads比ast.literal_eval更好?

vhipe2zx  于 2023-03-04  发布在  其他
关注(0)|答案(7)|浏览(293)

我有一个字典,它以字符串的形式存储在db字段中,我试图将它解析成一个dict,但是json.loads给了我一个错误。
为什么json.loads在这方面失败了而ast.literal_eval可以工作?是一个比另一个更好吗?

>>> c.iframe_data
u"{u'person': u'Annabelle!', u'csrfmiddlewaretoken': u'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}"

# json fails
>>> json.loads(c.iframe_data)
Traceback (most recent call last):
ValueError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)

# ast.literal_eval works
>>> ast.literal_eval(c.iframe_data)
{u'person': u'Annabelle!', u'csrfmiddlewaretoken': u'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}
des4xlb0

des4xlb01#

json.loads失败,因为您的c.iframe_data值不是有效的JSON文档。在有效的json文档中,字符串用双引号引起来,并且没有类似u的用于将字符串转换为Unicode的格式。
使用json.loads(c.iframe_data)意味着在c.iframe_data中反序列化JSON文档
ast.literal_eval在你需要eval来计算input表达式时使用。如果你有Python表达式作为你想要计算的输入。
一个比另一个更好吗?
这取决于数据。请参阅此answer了解更多上下文。

uinbv5nw

uinbv5nw2#

我有一个字典,它以字符串形式存储在db字段中。
这是一个设计上的错误,虽然完全有可能,就像有人已经做的那样,提取字典的repr,但不能保证对象的repr可以被求值。
在只存在字符串键、字符串值和数值的情况下,大多数时候Python eval函数将从其repr中重新生成值,但是我不知道为什么您认为这会使它成为有效的JSON。
我试图将其解析为一个dict,但是json.loads给了我一个错误。
当然,因为JSON没有存储在数据库中,所以期望它解析为JSON似乎是不合理的。虽然有趣的是,ast.literal_eval可以用来解析值,但除了相对简单的Python类型之外,没有任何保证。
既然你的数据看起来确实局限于这些类型,你的问题的真实的解决方案是纠正数据存储的方式,通过在存储到数据库之前将字典转换成一个字符串json.dumps.一些数据库系统(* 例如 *,PostgreSQL)有JSON类型,使查询这样的数据更简单,我建议你使用这样的类型,如果他们对你可用.
至于哪个“更好”,这将取决于具体的应用程序,但JSON被明确设计为一种紧凑的人类可读、机器可解析的格式,用于简单的结构化数据,而您当前的表示是基于Python特定的格式,(例如)在其他语言中很难计算,JSON是这里适用的标准,您将从使用它中受益。

93ze6v8z

93ze6v8z3#

json.loads专门用于解析JSON,这是一种非常严格的格式。没有u'...'语法,所有字符串都用双引号分隔,而不是用单引号分隔。使用json.dumps可以序列化json.loads可以读取的内容。
所以json.loads(string)json.dumps(object)的逆,而ast.literal_eval(string)(模糊地)是repr(object)的逆。
JSON很好,因为它是可移植的--几乎每种语言都有它的解析器,所以如果你想把JSON发送到Javascript前端,你不会有任何问题。
ast.literal_eval不容易移植,但功能稍微丰富一些:例如,可以使用元组、集合和字典,它们的键不限于字符串。
也叫json.loads is significantly faster than ast.literal_eval

oymdgrw7

oymdgrw74#

因为u"{u'person': u'Annabelle!', u'csrfmiddlewaretoken': u'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}"是Python Unicode字符串,而不是Javascript对象表示法,所以在chrome控制台中:

bad = {u'person': u'Annabelle!', u'csrfmiddlewaretoken': u'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}
SyntaxError: Unexpected string
good = {'person': 'Annabelle!', 'csrfmiddlewaretoken': 'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}
Object {person: "Annabelle!", csrfmiddlewaretoken: "wTE9RZGvjCh9RCL00pLloxOYZItQ98JN"}

或者可以使用Yaml来处理它:

>>> a = '{"person": "Annabelle!", "csrfmiddlewaretoken": "wTE9RZGvjCh9RCL00pLloxOYZItQ98JN"}'
>>> json.loads(a)
{u'person': u'Annabelle!', u'csrfmiddlewaretoken': u'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}
>>> import ast
>>> ast.literal_eval(a)
{'person': 'Annabelle!', 'csrfmiddlewaretoken': 'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}
>>> import yaml
>>> a = '{u"person": u"Annabelle!", u"csrfmiddlewaretoken": u"wTE9RZGvjCh9RCL00pLloxOYZItQ98JN"}'
>>> yaml.load(a)
{'u"person"': 'u"Annabelle!"', 'u"csrfmiddlewaretoken"': 'u"wTE9RZGvjCh9RCL00pLloxOYZItQ98JN"'}
>>> a = u'{u"person": u"Annabelle!", u"csrfmiddlewaretoken": u"wTE9RZGvjCh9RCL00pLloxOYZItQ98JN"}'
>>> yaml.load(a)
{'u"person"': 'u"Annabelle!"', 'u"csrfmiddlewaretoken"': 'u"wTE9RZGvjCh9RCL00pLloxOYZItQ98JN"'}
mftmpeh8

mftmpeh85#

首先,也是最重要的一点,不要对数据进行两次序列化。数据库本身就是一个数据序列化过程,它有一组丰富的、富有表现力的工具来查询、探索、操作和呈现数据。序列化数据以便随后放入数据库中,消除了孤立的子组件更新、子组件查询和索引的可能性,并将所有写入耦合到强制的初始读取。几个最重要的问题。
其次,Java Script Object Notation (JSON)是JavaScript语言的一个有限子集,适合于表示数据交换服务中的静态数据,作为 language 的一个子集,这意味着您可以在JS中简单地使用eval来重构原始对象。(没有诸如内部引用、模板定义类型扩展),具有内置的JavaScript语言的限制以及对使用需要大量“转义”的字符串的惩罚。结束标记的使用也使得它很难在纯流场景中使用,例如,你不能“终结”一个对象,直到命中它的配对},因此它也没有记录分隔标记。其他限制的显著例子包括在JSON中传递HTML需要过多的转义,所有数字都是浮点数(54位整数精度、舍入误差等),这显然不适合于存储或传输金融信息或使用需要64位整数、没有本机日期表示等的技术(例如加密)。
作为语言,JS和Python之间存在一些显著的差异,因此JSON的“JavaScript对象表示法”与PLS的区别也是如此(Python Literal Syntax)behavior.碰巧的是,为了定义文本,大多数JavaScript文本语法都直接与Python兼容,尽管解释略有不同。反之则不成立。请看上面的例子。2如果你关心的是为Python保持数据的保真度,Python常量比它们的JS等价物更有表现力,更少“损耗”。3然而,正如其他答案/评论所指出的,repr()不是生成该表示的可靠方式;Python的字面语法并不是要这样使用,为了最大程度的类型保真度,我通常推荐YAML序列化,JSON是它的一个完全有效的子集。
仅供参考,为了解决与实体关联的字典式Map的存储的实际问题,有entity-attribute-value data models。关系数据库FTW中的任意键值存储,但伴随权力而来的是责任。请谨慎使用此模式,并且仅在绝对需要时使用。(如果这是一种常见模式,请查看文档存储。)

fhity93d

fhity93d6#

json.loads应该比ast.literal_eval更适合解析JSON,原因如下(总结其他海报)。
在您的特定示例中,您的输入是非法的/格式错误的JSON,使用Python 2.x以错误的方式导出(所有不需要的和非法的u'前缀),无论如何,Python 2.x本身即将停产,请转到Python 3.x。您可以简单地使用正则表达式来修复/预处理:

>>> import json
>>> import re
>>> malformed_json = u"{u'person': u'Annabelle!', u'csrfmiddlewaretoken': u'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}"

>>> legal_json = re.sub(r'u\'([^\']*)\'', r'"\1"', malformed_json)
'{"person": "Annabelle!", "csrfmiddlewaretoken": "wTE9RZGvjCh9RCL00pLloxOYZItQ98JN"}'

>>> json.loads(legal_json)
{'person': 'Annabelle!', 'csrfmiddlewaretoken': 'wTE9RZGvjCh9RCL00pLloxOYZItQ98JN'}
  • (注意:如果您的架构中有大量格式错误的JSON字符串以错误的方式从Python 2.x导出,并存储在DB中,那么这不是不使用json.loads的合理理由,但可以重新访问您的架构。至少只需对所有字符串运行一次修复正则表达式,然后将法律的的JSON存储回去)

json.loads优点/缺点:

  • 处理所有法律的JSON,与ast.literal_eval不同
    ***慢。**有更快的JSON库,如ultrajson, yajl, simplejson等。此外,对于大型导入作业,您可以使用多处理/多线程(这也可以防止内存泄漏,这是所有解析器的常见问题)。
    ***数值字段:**将所有整数、长整数和浮点数转换为双精度,可能会丢失精度(@amcgregor)
3j86kqsm

3j86kqsm7#

在我的例子中,我跳过了ast.literal_eval(selected_cell_from_db),它不是从单引号引用的dict生成JSON dict(尽管它可以计算它,但它是string类型),我可以在包DeepDiff中使用它。
当保存对象时,我需要使用json.dumps(obj_to_save_to_db)而不是str(obj_to_save_to_db).json.dumps创建一个双引号的、json可读的字符串;str()不需要),
然后json.loads(selected_cell_from_db)

相关问题