从嵌套json阅读并获取None类型Error ->try/except

vbopmzt1  于 2023-01-06  发布在  其他
关注(0)|答案(1)|浏览(135)

我正在使用以下代码从嵌套的json阅读数据:

data = json.loads(json_file.json)
for nodesUni in data["data"]["queryUnits"]['nodes']:
        try:
            tm = (nodesUni['sql']['busData'][0]['engine']['engType'])
        except:
            tm = ''
        try:
            to = (nodesUni['sql']['carData'][0]['engineData']['producer']['engName'])
        except:
            to = ''
        json_output_for_one_GU_owner = {
            "EngineType": tm,
            "EngineName": to,
        }

我遇到了一个无类型错误的问题(例如,这个错误根本不存在nodesUni['sql']['busData'][0]['engine']['engType'],因为没有数据,所以我使用try/except。但我的代码更复杂,对每个值都使用try/except是疯狂的。有没有其他选项如何处理这个问题?
错误:“类型错误:“NoneType”对象不可订阅”

bjp0bcyl

bjp0bcyl1#

这并不简单,因为您的需求是无错误地遍历字典,并在最后得到一个空字符串值,所有这些都在一个非常简单的表达式中完成,就像级联[]操作符一样。

第一种方法

我的方法是在加载json文件时添加一个钩子,这样它就可以无限地创建缺省字典

import collections,json

def superdefaultdict():
    return collections.defaultdict(superdefaultdict)

def hook(s):
    c = superdefaultdict()
    c.update(s)
    return(c)

data = json.loads('{"foo":"bar"}',object_hook=hook)

print(data["x"][0]["zzz"])   # doesn't exist
print(data["foo"])  # exists

印刷品:

defaultdict(<function superdefaultdict at 0x000001ECEFA47160>, {})
bar

当访问一些不存在的键组合时(在任何级别),superdefaultdict递归地创建一个自身的defaultdict(这是一个很好的模式,您可以在Is there a standard class for an infinitely nested defaultdict?中了解更多),允许任意数量的不存在的键级别。
现在唯一的缺点是它返回一个defaultdict(<function superdefaultdict at 0x000001ECEFA47160>, {}),很难看。所以

print(data["x"][0]["zzz"] or "")

如果字典为空,则打印空字符串。这应该满足您的目的。
在你的语境中这样使用:

def superdefaultdict():
    return collections.defaultdict(superdefaultdict)

def hook(s):
    c = superdefaultdict()
    c.update(s)
    return(c)

data = json.loads(json_file.json,object_hook=hook)
for nodesUni in data["data"]["queryUnits"]['nodes']:
    tm = nodesUni['sql']['busData'][0]['engine']['engType'] or ""
    to = nodesUni['sql']['carData'][0]['engineData']['producer']['engName'] or ""

缺点:

  • 它在你的data对象中创建了很多空字典。这应该不是问题(除非你的内存非常低),因为对象不会被转储到一个文件中(不存在的值会出现在那里)
  • 如果某个值已经存在,尝试将其作为字典访问会导致程序崩溃
  • 同样,如果某个值是0或空列表,or操作符将选择""。这可以通过另一个 Package 器来测试对象是否为空superdefaultdict来解决。不太优雅,但可行。

第二种方法

将连续字典的访问转换为字符串(例如,将表达式(如"['sql']['busData'][0]['engine']['engType']")用双引号括起来,解析它,然后循环键以获取数据。如果出现异常,则停止并返回空字符串。

import json,re,operator

def get(key,data):
    key_parts = [x.strip("'") if x.startswith("'") else int(x) for x in re.findall(r"\[([^\]]*)\]",key)]
    try:
        for k in key_parts:
            data = data[k]
        return data
    except (KeyError,IndexError,TypeError):
        return ""

使用一些简单数据进行测试:

data = json.loads('{"foo":"bar","hello":{"a":12}}')

print(get("['sql']['busData'][0]['engine']['engType']",data))
print(get("['hello']['a']",data))
print(get("['hello']['a']['e']",data))

我们得到空字符串(缺少一些键),12(路径有效),空字符串(我们试图遍历一个非dict的现有值)。
语法可以简化(例如:"sql"."busData".O."engine"."engType"),但仍必须保留区分键(字符串)和索引(整数)的方法
第二种方法可能是最灵活的。

相关问题