whoisearth
whoisearth

Reputation: 4170

skipping a json key if does not exist

I'm running the following:

for server in server_list:
    for item in required_fields:
        print item, eval(item)

There is a possibility that some keys may not exist, but worse it's represented on a parent key not the one I'm scanning for.

So I'm scanning the json for the following key:

server['server_management']['server_total_cost_of_ownership']['description']

Which doesn't exist but it's actually the parent that is null:

server['server_management']['server_total_cost_of_ownership']

How do I write my code to account for this? It's not giving a key error. Right now I get the following traceback:

Traceback (most recent call last):
  File "C:/projects/blah/scripts/test.py", line 29, in <module>
    print item, eval(item)
  File "<string>", line 1, in <module>
TypeError: 'NoneType' object has no attribute '__getitem__'

Full code:

import csv
import json
import os
import requests
import sys

required_fields = ["server['server_name']","server['server_info']['asset_type']['display_name']",
                   "server['asset_status']['display_name']", "server['record_owner']['group_name']",
                   "server['server_management']['server_total_cost_of_ownership']['description']",
                   "server['server_management']['primary_business_owner']['name']",
                   "server['environment']['display_name']", "server['is_virtual']",
                   "server['managed_by']['display_name']", "server['server_info']['billable_ibm']",
                   "server['server_info']['billing_sub_type']['display_name']",
                   "server['server_info']['serial_number']", "server['location']['display_name']",
                   "server['inception_date']", "server['server_info']['decommission_date']" ]

# Query API for all servers
def get_servers_info():
    servers_info = requests.get('url')
    return servers_info.json()

def get_server_info(sid):
    server_info = requests.get('url')
    return server_info.json()

server_list = get_servers_info()
for server in server_list:
    for item in required_fields:
        print item, eval(item)

Upvotes: 1

Views: 4674

Answers (3)

MisterMiyagi
MisterMiyagi

Reputation: 50116

Your problem here is totally unrelated to using eval[1]. The exception you get is the same as if the code would have been there directly. What you are running (via eval) is:

a = server['server_management']
b = a['server_total_cost_of_ownership']
c = b['description']

Yet, b is None, so resolving it to c will fail. Like a KeyError, you can also catch a TypeError:

for server in server_list:
  for item in required_fields:
    try:
      print item, eval(item)
    except TypeError:
      print("Guess you're lucky you didn't include a fork bomb in your own code to eval.")

You may of course alternatively pass, print the offending item, open a browser to some page or do whatever error handling is appropriate given your input data.


[1] While not bickering around, I've made a new answer that works without eval. You can use precisely the same error handling:

for server in server_list:
  for item in required_fields:
  value = server
  for key in parse_fields(field):
    try:
      value = value[key]
    except TypeError:
      print("Remember Kiddo: Eval is Evil!")
      break
  else:  # for: else: triggers only if no break was issued
    print item, value

Upvotes: 0

David Yen
David Yen

Reputation: 78

Ignoring the context of the code and not understanding the use of eval here, the way to do this is to use .get() and seed it with reasonable defaults.

For example:

server['server_management']['server_total_cost_of_ownership']['description']

Can be:

server.get('server_management', {}).get('server_total_cost_of_ownership', {}).get('description', '')

Then if any of the keys do not exist you will always get back an empty description ''.

Upvotes: 1

Ali SAID OMAR
Ali SAID OMAR

Reputation: 6792

In fact you should avoid eval. After the json load since you know the key name, you can use a list to go deeper in the tree.

server['server_management']['primary_business_owner']['name']" => ["server_management', 'primary_business_owner', 'name']

Here a snippet for a json validation against a list of required fields.

data={
    "d": {
        "p":{
            "r":[
                "test"
            ]
        }
    },
    "a": 3
}


def _get_attr(dict_, attrs):
    try:
        src = attrs[:]
        root = attrs.pop(0)
        node = dict_[root]
        null = object()
        for i, attr in enumerate(attrs[:]):
            try:
                node = node.get(attr, null)
            except AttributeError:
                node = null
            if node is null:
                # i+2 pop and last element
                raise ValueError("%s not present (level %s)" % (attr, '->'.join(src[: i+2])))
        return node
    except KeyError:
        raise ValueError("%s not present" % root)

# assume list of required field
reqs = [
    ["d", "p", "r"],
    ["d"],
    ["k"],
    ["d", "p", "r", "e"],
]

for req in reqs:
    try:
        _get_attr(data, req)
    except ValueError as E:
        print(E)
# prints
# k not present
# e not present (level d->p->r->e)

Upvotes: 2

Related Questions