3

Given this json string, how can I pull out the value of id if code equals 4003?

error_json = '''{
    'error': {
        'meta': {
            'code': 4003,
            'message': 'Tracking already exists.',
            'type': 'BadRequest'
        },
        'data': {
            'tracking': {
                'slug': 'fedex',
                'tracking_number': '442783308929',
                'id': '5b59ea69a9335baf0b5befcf',
                'created_at': '2018-07-26T15:36:09+00:00'
            }
        }
    }
}'''

I can't assume that anything other than the error element exists at the beginning, so the meta and code elements may or may not be there. The data, tracking, and id may or may not be there either.

The question is how to extract the value of id if the elements are all there. If any of the elements are missing, the value of id should be None

Kevin Bedell
  • 13,254
  • 10
  • 78
  • 114

3 Answers3

6

A python dictionary has a get(key, default) method that supports returning a default value if a key is not found. You can chain empty dictionaries to reach nested elements.

# use get method to access keys without raising exceptions 
# see https://docs.quantifiedcode.com/python-anti-patterns/correctness/not_using_get_to_return_a_default_value_from_a_dictionary.html
code = error_json.get('error', {}).get('meta', {}).get('code', None)

if code == 4003:
    # change id with _id to avoid overriding builtin methods
    # see https://docs.quantifiedcode.com/python-anti-patterns/correctness/assigning_to_builtin.html
    _id = error_json.get('error', {}).get('data', {}).get('tracking', {}).get('id', None)

Now, given a string that looks like a JSON you can parse it into a dictionary using json.loads(), as shown in Parse JSON in Python

bluesmonk
  • 1,237
  • 13
  • 31
  • Because is cleaner, and also because it is a built-in method that doesn't raise exceptions. See https://docs.quantifiedcode.com/python-anti-patterns/correctness/not_using_get_to_return_a_default_value_from_a_dictionary.html. Also, what do you mean with a meaningful result? – bluesmonk Jul 27 '18 at 03:56
  • 1
    It looks like Mad Scientist is criticizing and downvoting others. – Kevin Bedell Jul 27 '18 at 03:58
  • @Kevin. Only one down vote. I'll be glad to rescind anything I say with an apology if needed if I'm totally off base. I'm trying to understand, not be unpleasant. – Mad Physicist Jul 27 '18 at 04:00
  • I'll start by removing my initial comment here. I can see how you'd find it abrasive. – Mad Physicist Jul 27 '18 at 04:01
  • By meaningful result I meant non-None. I was implying that your solution forgoes the benefit of short circuiting that this lookup offers and I'm wondering why you chose to do that. – Mad Physicist Jul 27 '18 at 04:02
  • I actually like these one-liner assignments. Pretty safe, readable and to the point. But this is missing the conditional. The OP just wants an id `if` the code is 4003. – JAponte Jul 27 '18 at 04:03
  • If `id` is present, it will return the value of `dict_obj[id]`. OP clearly stated that if the `id` key is not present then the value of `id` should be `None`. I don't get your point. – bluesmonk Jul 27 '18 at 04:04
  • @JAponte you are right and I was lazy. I edited my answer – bluesmonk Jul 27 '18 at 04:08
  • @KevinBedell upvoted, to mitigate unwarranted downvotes. I think the question is valid and I learned some nice python syntax tonight thanks to it. – JAponte Jul 27 '18 at 04:12
2

I would try this:

import json

error_json = '''{
    "error": {
        "meta": {
            "code": 4003,
            "message": "Tracking already exists.",
            "type": "BadRequest"
        },
        "data": {
            "tracking": {
                "slug": "fedex",
                "tracking_number": "442783308929",
                "id": "5b59ea69a9335baf0b5befcf",
                "created_at": "2018-07-26T15:36:09+00:00"
            }
        }
    }
}'''

parsed_json = json.loads(error_json)

try:
    if parsed_json["error"]["meta"]["code"] == int(parsed_json["error"]["meta"]["code"]):
        print(str(parsed_json["error"]["data"]["tracking"]["id"]))
except:
    print("no soup for you")

Output:

5b59ea69a9335baf0b5befcf

A lot of python seems to be it's better to ask for forgiveness instead of permission. You could look up to see if that key in the dictionary is there, but really it's easier to just try. I'm specifically doing a check to make sure that code is an int, but you could change it around any way you'd like. If it can be other things you'd have to adjust it. There are several different solutions to this, it's really whatever you feel the most comfortable doing and maintaining.

sniperd
  • 5,124
  • 6
  • 28
  • 44
0

You can add a check for key errors to the whole operation:

def get_id(j)
    try:
        if j['error']['meta’]['code'] == 4003:
            return j['error']['data']['tracking']['id']
    except KeyError:
        pass
    return None

If any element is missing, this function will quietly return None. If all the required elements are present, it will return the required ID. The only time it could really fail is if one of the intermediate keys does not refer to a dictionary. That could potentially result in a TypeError.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264