TypeError:使用Python解析JSON时字符串索引必须是整数?
Posted
技术标签:
【中文标题】TypeError:使用Python解析JSON时字符串索引必须是整数?【英文标题】:TypeError: string indices must be integers while parsing JSON using Python? 【发布时间】:2013-12-03 18:53:01 【问题描述】:我现在很困惑为什么我不能解析这个 JSON 字符串。类似的代码适用于其他 JSON 字符串,但不适用于这个 - 我正在尝试解析 JSON 字符串并从 JSON 中提取脚本。
下面是我的代码。
for step in steps:
step_path = '/example/v1' +'/'+step
data, stat = zk.get(step_path)
jsonStr = data.decode("utf-8")
print(jsonStr)
j = json.loads(json.dumps(jsonStr))
print(j)
shell_script = j['script']
print(shell_script)
所以第一个print(jsonStr)
会打印出这样的东西 -
"script":"#!/bin/bash\necho Hello world1\n"
第二个print(j)
会打印出类似这样的内容 -
"script":"#!/bin/bash\necho Hello world1\n"
然后第三个打印没有被打印出来,它给出了这个错误 -
Traceback (most recent call last):
File "test5.py", line 33, in <module>
shell_script = j['script']
TypeError: string indices must be integers
所以我想知道我在这里做错了什么?
我已经使用上面相同的代码来解析 JSON,它工作正常..
【问题讨论】:
第三次的预期输出是多少? 它应该从 JSON 字符串中提取脚本部分......所以它应该打印出#!/bin/bash\necho Hello world1\n
。对吗?
【参考方案1】:
好吧...所以对于那些因为习惯了 JS 而仍然迷路的人来说,这是我在测试了多个用例后所理解的:
json.dumps
不会使您的字符串准备好加载json.loads
。它只会将其编码为 JSON 规范(通过在几乎所有地方添加转义)!
json.loads
会将格式正确的 JSON 字符串转换为 python 字典。只有当 JSON 遵循 JSON 规范(没有单引号、布尔值的首字母大写等)时,它才会起作用。
转储 JSON - 编码故事
举个例子吧!
$ obj = "foobar": True
这不是 json !这是一个使用 Python 类型(如布尔值)的 Python 字典。
True
与 JSON 规范不兼容,因此要将其发送到 API,您必须将其序列化为 REAL JSON。这就是json.dumps
的用武之地!
$ json.dumps("foobar": True)
'"foobar": true'
看到了吗? True
变成了 true
,这是真正的 JSON。您现在有了一个可以发送到现实世界的字符串。干得好。
加载 JSON - 解码故事
那么现在让我们谈谈json.loads
。
你有一个看起来像 json 的字符串,但它只是一个字符串,你想要的是一个 python 字典。让我们来看看下面的例子:
$ string = '"foobar": true'
$ dict = json.loads(string)
'foobar': True
这里我们有一个看起来像 JSON 的字符串。您可以使用json.loads
在字典中转换此字符串并执行dict["foobar"]
,这将返回True
。
那么,为什么会有这么多错误呢?
好吧,如果你的 JSON 看起来像 JSON,但不是真正的 JSON 兼容(规范方面),例如:
$ string = "'foobar': true"
$ json.loads(string)
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes
砰!这不起作用,因为 JSON 规范不允许您使用单引号,但只能使用双引号...
如果您将引号反转为'"foobar": true'
,那么它将起作用。
你可能尝试过的是:
string = json.loads(json.dumps("'foobar': true"))
这个 JSON 是无效的(检查引号),而且你会得到一个字符串作为结果。失望?我知道...
json.dumps
将修复您的 JSON 字符串,但也会对其进行编码。即使现在可以使用 JSON,编码也会使 json.loads
无用。
您必须了解json.dumps
编码和json.loads
解码!
所以你在这里做的是编码一个字符串,然后解码这个字符串。但它仍然是一个字符串!你没有做任何事情来改变这个事实!如果你想从 string 到 dictionary 得到它,那么你需要一个额外的步骤... => 第二个json.loads
!
让我们尝试使用有效的 JSON(不是单引号)
$ obj = json.loads(json.loads(json.dumps('"foobar": true')))
$ obj["foobar"]
True
json 字符串经过json.dumps
并被编码。然后它通过json.loads
被解码(没用......是的)。最后,它再次通过json.loads
并从字符串转换为字典。如您所见,在这种情况下使用json.dumps
只会增加一个无用的步骤。
最后一件事。如果我们再次做同样的事情,但 JSON 错误:
$ string = json.loads(json.loads(json.dumps("'foobar': true")))
json.decoder.JSONDecodeError: Expecting property name enclosed in double quotes
这里的引号是错误的(你现在还没有习惯吗?)。
这里发生的是json.dumps
修复了您的 JSON。 json.loads
删除了修复程序(哈哈),最后 json.loads
得到了错误的 JSON,由于前两个步骤相互取消,该 JSON 没有改变。
TL;DR
总结:
自己修复 JSON !不要给 json.loads
错误格式的 JSON,也不要尝试将 json.loads
与 json.dumps
混合来修复只有你可以修复的问题。
希望这对某人有所帮助;-)
免责声明。我不是 python 专家。 随意在评论部分挑战这个答案。
【讨论】:
【参考方案2】:问题在于 jsonStr 是一个字符串,它在 JSON 中编码某些对象,而不是实际对象。
你显然知道它是一个字符串,因为你称它为jsonStr
。这条线有效的事实证明了这一点:
jsonStr = data.decode("utf-8")
所以,jsonStr
是一个字符串。在字符串上调用json.dumps
是完全合法的。该字符串是某个对象的 JSON 编码还是您的姓氏都没有关系;您可以在 JSON 中对该字符串进行编码。然后你可以解码那个字符串,取回原来的字符串。
所以,这个:
j = json.loads(json.dumps(jsonStr))
... 将在j
中返回与jsonStr
完全相同的字符串。您还没有解码到原始对象。
为此,不要进行额外的编码:
j = json.loads(jsonStr)
如果不清楚,请尝试使用交互式终端:
>>> obj = ['abc', 'a': 1, 'b': 2]
>>> type(obj)
list
>>> obj[1]['b']
2
>>> j = json.dumps(obj)
>>> type(j)
str
>>> j[1]['b']
TypeError: string indices must be integers
>>> jj = json.dumps(j)
>>> type(jj)
str
>>> j
'["abc", "a": 1, "b": 2]'
>>> jj
'"[\\"abc\\", \\"a\\": 1, \\"b\\": 2]"'
>>> json.loads(j)
['abc', 'a': 1, 'b': 2]
>>> json.loads(j) == obj
True
>>> json.loads(jj)
'["abc", "a": 1, "b": 2]'
>>> json.loads(jj) == j
True
>>> json.loads(jj) == obj
False
【讨论】:
赞成:因为您所做的不仅仅是说用 b 替换 a...您解释了一些原因并尝试展示如何调试的示例...【参考方案3】:尝试将j = json.loads(json.dumps(jsonStr))
替换为j = json.loads(jsonStr)
。
【讨论】:
可能j
与jsonStr
相同,是一个字符串。使用字符串键对其进行索引会失败,但您描述的异常除外。省略dumps
调用使loads
调用将原始字符串转换为字典,然后您可以按键索引。如果您将 print(j)
呼叫转换为 print(repr(j))
,您将能够看到差异。
我不明白。当我尝试执行json.loads(string_value)
时,它确实如此,恕我直言,它应该引发ValueError Exception: No JSON object could be decoded
,因为 string_value 只是一个字符串而不是 JSON 对象。
@tMJ:如果jsonStr
是JSON中表示对象的字符串,那么json.dumps(jsonStr)
是JSON中表示(JSON中表示对象的字符串)的字符串。这是完全有效的,但是当你 loads
它时,你会得到一个表示 JSON 对象的字符串;您必须loads
两次才能取回原始对象。
@abarnert 那么调用zk.get
返回的值,实际上是一个JSON对象吗?
@tMJ:没有“JSON 对象”这样的东西。当人们这么说时,一半的意思是“可以用 JSON 编码的 Python(或 javascript 或其他)对象”,一半的意思是“一个字符串,它是对象的 JSON 表示形式”。我不知道你指的是哪一个。我怀疑你也没有。只是不要使用这个词。显然zk.get
返回的值是一个bytes
对象,因为你可以在它上面调用.decode('utf-8')
。大概是字符串的 UTF-8 编码是对象的 JSON 表示。以上是关于TypeError:使用Python解析JSON时字符串索引必须是整数?的主要内容,如果未能解决你的问题,请参考以下文章
TypeError:列表索引必须是整数或切片,而不是解析json请求时的str
Python TypeError:“_csv.writer”对象不可迭代
TypeError:对象在 DJango 1.8 Python 3.4 中不是 JSON 可序列化的