Python字符串转字典:json、eval与literal_eval的对比与安全实践

Nora
Nora
发布时间:2026-10-13 | 5人浏览过
原创

本文对比了Python中将字符串转换为字典的三种方法:json.loads、eval和ast.literal_eval。json.loads要求严格的双引号格式,eval存在严重的安全执行风险,而ast.literal_eval既能兼容单引号又保证安全,是生产环境中的推荐方案。

Python字符串转字典的常见方法

在处理数据时,常需要将字符串类型的字典转换为Python的dict对象。起初,最直接的想法是使用json模块进行处理,但在实际应用中可能会遇到一些意想不到的问题。

使用json模块转换

首先介绍通过json模块进行转换的标准做法。


In [1]: import json
In [2]: mes = '{"InsId": 2, "name": "lege-happy", "CreationTime": "2019-04-23T03:18:02Z"}'
In [3]: mes_to_dict = json.loads(mes)
In [4]: print type(mes_to_dict)
<type 'dict'>

上述方法在字符串格式规范时是可行的,但如果输入的字符串mes格式为mes = '{"InsId": 2, "name": "lege-happy", "CreationTime": "2019-04-23T03:18:02Z"}',即使用单引号包裹键名和值,直接调用json模块进行转换会发生什么?


In [5]: import json

In [6]: mes = "{'InsId': 1, 'name': 'lege-error', 'CreationTime': '2019-04-24T03:18:02Z'}"

In [7]: mes_to_dict = json.loads(mes)
---------------------------------------------------------------------------
ValueError                Traceback (most recent call last)
<ipython-input-7-77264851f35b> in <module>()
----> 1 mes_to_dict = json.loads(mes)

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/__init__.pyc in loads(s, encoding, cls, object_hook, parse_float, parse_int, parse_constant, object_pairs_hook, **kw)
  336       parse_int is None and parse_float is None and
  337       parse_constant is None and object_pairs_hook is None and not kw):
--> 338     return _default_decoder.decode(s)
  339   if cls is None:
  340     cls = JSONDecoder

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.pyc in decode(self, s, _w)
  364
  365     """
--> 366     obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  367     end = _w(s, end).end()
  368     if end != len(s):

/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/json/decoder.pyc in raw_decode(self, s, idx)
  380     """
  381     try:
--> 382       obj, end = self.scan_once(s, idx)
  383     except StopIteration:
  384       raise ValueError("No JSON object could be decoded")

ValueError: Expecting property name: line 1 column 2 (char 1)

由此可见,使用json模块进行转换存在一个明显的格式限制。

根据JSON语法规范,数组或对象中的字符串必须使用双引号("),严禁使用单引号(')。官方文档明确指出:"A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes"。因此,当字符串使用单引号时,上述转换方式会报错。

使用eval函数的风险

接下来介绍另一种转换方式:使用eval函数。


In [8]: mes = '{"InsId": 2, "name": "lege-happy", "CreationTime": "2019-04-23T03:18:02Z"}'

In [9]: mes_dict = eval(mes)

In [10]: print type(mes_dict)
<type 'dict'>

In [11]:

In [11]: mes = mes = "{'InsId': 1, 'name': 'lege-error', 'CreationTime': '2019-04-24T03:18:02Z'}"

In [12]: mes_dict = eval(mes)

In [13]: print type(mes_dict)
<type 'dict'>

从上面的示例可以看出,使用eval进行转换时,不再受限于json的双引号格式要求,能够成功解析单引号字符串。但我们需要特别注意,使用eval函数存在极大的安全隐患,例如:

eval会直接计算字符串中的表达式。例如,输入'1+1',它会直接计算出结果4。


In [14]: value = eval(raw_input('please input a value string:'))
please input a value string:2 + 2

In [15]: value
Out[15]: 4

从上述例子来看,eval的功能非常强大:它不仅可以实现string与list、tuple、dict之间的类型转换,还可以充当计算器。更甚者,它可以处理任何它能解析的字符串表达式,而不顾及其可能带来的后果。因此,eval强大的背后隐藏着巨大的安全隐患。

假设用户恶意输入了包含系统命令的字符串,例如:


open(r'D://filename.txt', 'r').read()
__import__('os').system('dir')
__import__('os').system('rm -rf /etc/*')

使用ast.literal_eval的安全方案

此时,eval会执行该字符串中的命令,导致显示电脑目录结构、读取文件,甚至执行删除文件等危险操作。如果是格式化磁盘等更严重的操作,eval也会照做不误。这显然不符合我们的数据处理需求。 接下来介绍通过literal_eval进行转换的方法。


In [20]: import ast
In [21]: mes = '{"InsId": 2, "name": "lege-happy", "CreationTime": "2019-04-23T03:18:02Z"}'
In [22]: mes_dict = ast.literal_eval(mes)
In [23]: print type(mes_dict)
<type 'dict'>
In [24]:
In [24]:
In [24]: mes = mes = "{'InsId': 1, 'name': 'lege-error', 'CreationTime': '2019-04-24T03:18:02Z'}"
In [25]: mes_dict = ast.literal_eval(mes)
In [26]: print type(mes_dict)
<type 'dict'>

使用ast模块中的literal_eval函数进行转换,既解决了json模块对双引号的严格要求,又避免了eval函数的安全性问题。因此,官方推荐使用ast.literal_eval。

我们来看看官方文档对literal_eval的描述:


def literal_eval(node_or_string):
  """
  Safely evaluate an expression node or a string containing a Python
  expression. The string or node provided may only consist of the following
  Python literal structures: strings, numbers, tuples, lists, dicts, booleans,
  and None.
  """

这意味着ast模块旨在帮助Python应用程序处理抽象语法树(AST)。该模块下的literal_eval()函数会判断需要计算的内容在计算后是否构成合法的Python数据类型。如果是合法的Python字面量结构(如字符串、数字、元组、列表、字典、布尔值和None),则进行转换;否则拒绝执行。

例如,对于上述的计算操作及危险操作,如果替换为ast.literal_eval(),,程序都会拒绝执行,从而确保安全性。

因此,个人建议在将字符串转换为dict时,出于安全考虑,最好使用ast.literal_eval()函数。

总结

以上介绍了Python中将字符串转换为字典的几种方法及其优缺点。希望本文能对大家有所帮助,如有任何疑问,欢迎留言交流,小编会及时回复。在此非常感谢大家对本站的支持! 如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表