2

I have a python object that looks like this. I am trying to parse this object and turn it to a human readable string which I need to put in the logs. How can I recursively loop through this considering the object could be nested dictionaries or nested lists or dictionaries inside lists inside dictionaries etc.

{"plugins": 
  [
    {"Chrome PDF Viewer": "mhjfbmdgcfjbbpaeojofohoefgiehjai"}, 
    {"Chrome PDF Viewer": "internal-pdf-viewer"}, 
    {"Native Client": "internal-nacl-plugin"}, 
    {"Shockwave Flash": "PepperFlashPlayer.plugin"}, 
    {"Widevine Content Decryption Module": "widevinecdmadapter.plugin"}
  ]
}

I want to possibly serialize the above to look something like this

"plugins: 
     Chrome PDF Viewer": "mhjfbmdgcfjbbpaeojofohoefgiehjai, 
     Chrome PDF Viewer": "internal-pdf-viewer, 
     Native Client": "internal-nacl-plugin, 
     Shockwave Flash": "PepperFlashPlayer.plugin, 
     Widevine Content Decryption Module": "widevinecdmadapter.plugin"

My code so far [this works for nested dictionaries but I am not sure how I can alter this to support lists in the above object]:

result_str = ""

def dictionary_iterator(results):
    global result_str
    for key, value in results.items():
        if isinstance(value, dict):
            result_str = result_str + key + ": \n \t"
            dictionary_iterator(value)
        else:
            result_str = result_str + key + ": " + str(value) + "\n"

    return result_str

I have looked over possible answers but could not find a solution.

smriti
  • 1,073
  • 1
  • 13
  • 25
  • The json library can do this for you: https://docs.python.org/2/library/json.html – Feodoran Jul 18 '17 at 20:50
  • Thanks for the suggestion but I don't want to use that cause I want to do additional things that json library wont let me. – smriti Jul 18 '17 at 20:53
  • 1
    Could you please fix indentation. Also please describe what do you want to iterate over. When you say _"I am trying to parse this object and turn it to a human readable string..."_ it is not clear *what* are you trying to parse and *how* do you want parsed information to be assembled into a string. Is there anything wrong with your code? Does it work as expected but you would like to improve it? – AGN Gazer Jul 18 '17 at 20:54
  • Why do you need `global result_str` inside this function? You are explicitly returning it! – AGN Gazer Jul 18 '17 at 20:57
  • @smriti, post the desired result – RomanPerekhrest Jul 18 '17 at 20:58
  • I find JSON quite human-readable, it is unambiguous. – trincot Jul 18 '17 at 20:59
  • You better check for `isinstance(value, collections.Iterable)` (see related SO question [here](https://stackoverflow.com/questions/3023503/how-can-i-check-if-an-object-is-an-iterator-in-python). – agtoever Jul 18 '17 at 21:00
  • Possible duplicate of [How can I check if an object is an iterator in Python?](https://stackoverflow.com/questions/3023503/how-can-i-check-if-an-object-is-an-iterator-in-python) – agtoever Jul 18 '17 at 21:00
  • Your sample output isn't particularly clear, especially in the weird use of quotation marks. – Prune Jul 18 '17 at 21:05
  • You may want to search on the term "flatten", which is much of what you're trying to do. – Prune Jul 18 '17 at 21:05
  • the quotations mark are to show that I am turning the object to a string, cause I need the output to be a string. I hope that makes sense – smriti Jul 18 '17 at 21:07
  • @smriti: Is there really any need for recursion ? – Maurice Meyer Jul 18 '17 at 21:12
  • @MauriceMeyer yes cause the function thats calling this function is passing n number of results of various types. For example another result that is being passed to the function is like {"enabled": true}}, {"status": "Warning", "expected": "", "message": "Failed to upgrade websocket connection", "name": "Verify websocket support", "results": {}}. Makes sense? – smriti Jul 18 '17 at 21:18
  • You have provided one potential *input*, it sounds like you want to account for other *forms* of input - could you show, minimal, examples of those other forms? – wwii Jul 18 '17 at 21:27

3 Answers3

2

Maybe the output of pformat would suit you:

from pprint import pformat
results_str = pformat(results)
kristaps
  • 1,705
  • 11
  • 15
2

The formatting might be a bit off

def humanizer(input, result=''):
    if type(input) == dict:
        for k, v in input.items():
            if type(v) == str:
                result += '%s:%s\n\t' % (str(k), str(v))
            elif type(v) in (dict, list):
                result += '%s:\n\t' % str(k)
                result = humanizer(v, result)
                result += '\n\t'
    elif type(input) == list:
        for item in input:
            if type(item) == str:
                result += item
                continue
            result = humanizer(item, result) + '\n\t'
    else:
        result += input + '\n\t'
    return result

Result:

plugins:
        Chrome PDF Viewer:mhjfbmdgcfjbbpaeojofohoefgiehjai

        Chrome PDF Viewer:internal-pdf-viewer

        Native Client:internal-nacl-plugin

        Shockwave Flash:PepperFlashPlayer.plugin

        Widevine Content Decryption Module:widevinecdmadapter.plugin
Raunak Agarwal
  • 7,117
  • 6
  • 38
  • 62
  • You could also use [isinstance](https://docs.python.org/3/library/functions.html#isinstance) to validate the type. – wwii Jul 18 '17 at 21:25
1

You need an elif-condition in case of type == list and a indentation tracker:

data = {"plugins": 
  [
    {"Chrome PDF Viewer": "mhjfbmdgcfjbbpaeojofohoefgiehjai"}, 
    {"Chrome PDF Viewer": "internal-pdf-viewer"}, 
    {"Native Client": "internal-nacl-plugin"}, 
    {"Shockwave Flash": "PepperFlashPlayer.plugin"}, 
    {"Widevine Content Decryption Module": "widevinecdmadapter.plugin"}
  ],
  "anotherLevel":
    {
        "sublevel": [
            {'item1': 'value1'}
        ]
    }
}

result_str = ""
def dictionary_iterator(indent, data):
    global result_str

    if isinstance(data, dict):
        for key, value in data.items():
            result_str += indent*'\t' + key + '\n'
            indent = indent + 1
            dictionary_iterator(indent, value)

    elif isinstance(data, list):
        for item in data:
            if isinstance(item, dict) and len(list(item.keys())) == 1:
                key = list(item.keys())[0]
                value = item[key]
                result_str += indent*'\t' + key + ': ' + value + '\n'
            else:
                indent = indent + 1
                dictionary_iterator(indent, item)
    return result_str


if __name__ == '__main__':
    print(dictionary_iterator(0, data))

That will print out:

plugins
    Chrome PDF Viewer: mhjfbmdgcfjbbpaeojofohoefgiehjai
    Chrome PDF Viewer: internal-pdf-viewer
    Native Client: internal-nacl-plugin
    Shockwave Flash: PepperFlashPlayer.plugin
    Widevine Content Decryption Module: widevinecdmadapter.plugin
    anotherLevel
        sublevel
            item1: value1
Maurice Meyer
  • 17,279
  • 4
  • 30
  • 47
  • Why do you need a global var if you return it anyway? Global _varaibles_ are a code smell. – 9000 Jul 21 '17 at 13:04