Search code examples
pythonjsonsimplejson

How to properly parse JSON with simplejson?


I can have the following JSON string:

{ "response" : [ [ { "name" : "LA_",
          "uid" : 123456
        } ],
      [ { "cid" : "1",
          "name" : "Something"
        } ],
      [ { "cid" : 1,
          "name" : "Something-else"
        } ]
    ] }

or one of the following:

{"error":"some-error"}

{ "response" : [ [ { "name" : "LA_",
          "uid" : 123456
        } ],
      [ { "cid" : "1",
          "name" : ""
        } ],
      [ { "cid" : 1,
          "name" : "Something-else"
        } ]
    ] }

{ "response" : [ [ { "name" : "LA_",
          "uid" : 123456
        } ] ] }

So, I am not sure if all childs and elements are there. Will it be enough to do the following verifications to get Something value:

if jsonstr.get('response'):
    jsonstr = jsonstr.get('response')[1][0]
    if jsonstr:
        name = jsonstr.get('name')
        if jsonstr: # I don't need empty value
            # save in the database

Can the same be simplified?


Solution

  • You're not guaranteed that the ordering of your inner objects will be the same every time you parse it, so indexing is not a safe bet to reference the index of the object with the name attribute set to Something.

    Instead of nesting all those if statements, you can get away with using a list comprehension. Observe that if you iterate the response key, you get a list of lists, each with a dictionary inside of it:

    >>> data = {"response":[[{"uid":123456,"name":"LA_"}],[{"cid":"1","name":"Something"}],[{"cid":1,"name":"Something-else"}]]}
    >>> [lst for lst in data.get('response')]
    [[{'name': 'LA_', 'uid': 123456}], [{'name': 'Something', 'cid': '1'}], [{'name': 'Something-else', 'cid': 1}]]
    

    If you index the first item in each list (lst[0]), you end up with a list of objects:

    >>> [lst[0] for lst in data.get('response')]
    [{'name': 'LA_', 'uid': 123456}, {'name': 'Something', 'cid': '1'}, {'name': 'Something-else', 'cid': 1}]
    

    If you then add an if condition into your list comprehension to match the name attribute on the objects, you get a list with a single item containing your desired object:

    >>> [lst[0] for lst in data.get('response') if lst[0].get('name') == 'Something']
    [{'name': 'Something', 'cid': '1'}]
    

    And then by indexing the first item that final list, you get the desired object:

    >>> [lst[0] for lst in data.get('response') if lst[0].get('name') == 'Something'][0]
    {'name': 'Something', 'cid': '1'}
    

    So then you can just turn that into a function and move on with your life:

    def get_obj_by_name(data, name):
        objects = [lst[0] for lst in data.get('response', []) if lst[0].get('name') == name]
        if objects:
            return objects[0]
    
        return None
    
    print get_obj_by_name(data, 'Something')
    # => {'name': 'Something', 'cid': '1'}
    
    print get_obj_by_name(data, 'Something')['name']
    # => 'Something'
    

    And it should be resilient and return None if the response key isn't found:

    print get_obj_by_name({"error":"some-error"}, 'Something')
    # => None