首页 > 解决方案 > 为特定 id 值生成 json 输出时出错

问题描述

我有以下 json 树。

json_tree ={
  "Garden": {
    "Seaside": {
      "@loc": "127.0.0.1",
      "@myID": "1.3.1",
      "Shoreside": {
        "@myID": "3",
        "InfoList": {
          "Notes": {
            "@code": "0",
            "@myID": "1"
          },
          "Count": {
            "@myID": "2",
            "@val": "0"
          }
        },
        "state": "0",
        "Tid": "3",
        "Lakesshore": {
          "@myID": "4",
          "InfoList": {
            "Notes": {
              "@code": "0",
              "@oid": "1"
            },
            "Count": {
              "@myID": "2",
              "@val": "0"
            }
          },
          "state": "0",
          "Tid": "4"
        }
      },
      "state": "0",
      "Tid": "2"
    },
    "Tid": "1",
    "state": "0"
  }
}

我有一个方法,它接受“Tid”值并以以下格式返回输出。

这就是问题所在。我不明白为什么对于 的值Tid = 2,我得到“错误”,说明InfoList不存在。对于其他 Tid 值,它运行良好。有人可以帮我解决这个问题吗?

“ Tid InfoList:”2 处没有,但我不确定如何更新我的逻辑来处理这个问题。

def get_output (d, id):
    if isinstance(d, dict) and d.get('id') == id:
        yield {"Tid": d['Tid'], "Notes": d['InfoList']['Notes']['@code'], "status": d['state']}
    for i in getattr(d, "values", lambda: [])():
        yield from get_based_on_id(i, id)

# The id is from 2 and higher
key_list = list(get_output (json_tree, id))
# To create the json result
jsonify(key_list if not key_list else key_list[0])
For "Tid" values of 2 and higher the get_output method creates this output:

{
  "Tid": "3", 
  "Notes": "2000", 
  "state": "2"
}

下面显示的这部分效果很好。问题仅在于上面显示的代码。

def get_output_id_1 (d, id):   
    if isinstance(d, dict) and d.get('id') == id:
        yield {"id": d['Tid'], "state": d['state']}
    for i in getattr(d, "values", lambda: [])():
        yield from get_root_id(i, id)
For "Tid" value of 1 and higher the get_output_id_1 method creates this output:

{
  "Tid": "1", 
  "state": "1", 
}

任何帮助表示赞赏。

标签: pythonjson

解决方案


问题是您正在使用直接访问来利用可能在也可能不在字典中的键。要解决此问题,请使用该dict.get方法,该方法将返回None或您指定的一些默认值,以防密钥不存在:

small_example = {
    'Tid': '2', 
    'status': 'some status'
}

# there is no InfoList key here, so to get around that, I can use something like:
info_list = small_example.get('InfoList')

repr(info_list)
None

get现在,如果需要将事物链接在一起,您可以指定默认返回值,例如使用嵌套字典调用:

{
    'Tid': small_example['Tid'],
    'Notes': small_example.get('InfoList', {}).get('Notes', {}).get('@code'),
    'status': small_example.get('state')
}

看看在前两个调用中,我如何返回一个空字典以防万一InfoList和/或Notes丢失,它支持后续调用get. 没有它,我会得到一个AttributeError

small_example.get('InfoList').get('Notes')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'get'

所以你的yield陈述应该是这样的:

yield {
    "Tid": d['Tid'], 
    "Notes": d.get('InfoList', {}).get('Notes', {}).get('@code'), 
    "status": d.get('state')
}

编辑:如果你想要一个不同的默认值Notes怎么办?

这有点棘手,特别是如果您想要一个不支持的数据结构.get,例如str.

您的yield语句可能必须由不同的函数生成,以使事情更整洁:

# d is expected to be a dictionary
def create_yield(d):
    # I'm using direct access on `Tid` because I'm assuming it should
    # always be there, and if not it will raise a KeyError, you can
    # modify this to fit your use case
    container = {'Tid': d['Tid'],
                 'status': d.get('state')}

    notes = small_example.get('InfoList', {}).get('Notes')
    # if notes is a dict (not None), then we can get `@code` from it
    if notes is not None:
        container['Notes'] = notes.get('@code')
    # otherwise, don't set the `Notes` attribute

    return container



# later in your code at your yield statement
# you can call this function instead of manually building the dictionary
yield create_yield(small_example)

推荐阅读