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 解码!

所以你在这里做的是编码一个字符串,然后解码这个字符串。但它仍然是一个字符串!你没有做任何事情来改变这个事实!如果你想从 stringdictionary 得到它,那么你需要一个额外的步骤... => 第二个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.loadsjson.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)

【讨论】:

可能jjsonStr 相同,是一个字符串。使用字符串键对其进行索引会失败,但您描述的异常除外。省略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解析多层嵌套的JSON?

Python TypeError:“_csv.writer”对象不可迭代

TypeError:对象在 DJango 1.8 Python 3.4 中不是 JSON 可序列化的

TypeError:'int'对象不可迭代,使用 Python 3 [关闭]

在 Python 中读取 JSON 字符串:接收错误“TypeError:字符串索引必须是整数”