
I’ve never ever used a recursion, and I never thought I will. So far, loops were enough.
dashboarddashboard which posses a given key, "expr" in my casedashboarddashboard contains an unknown number of dicts and lists, which are nested, and you don’t really know the structure of this dashboard, it’s totally unknown before execution timeThe data I was working on - Grafana Node-Exporter Dashboard - https://grafana.com/api/dashboards/1860/revisions/19/download (JSON file)
json to import the file, and saved the file’s data in a variable called dashboardget_expressions but you can call it whatever you wantHere’s the code
import json
with open("node-exporter-full_rev19.json", "r") as file:
dashboard = json.load(file)
def get_expressions(obj, search_key, my_list=[]):
if isinstance(obj, list) and len(obj):
for item in obj:
get_expressions(item, search_key, my_list)
elif isinstance(obj, dict):
if search_key in obj and obj[search_key]:
return my_list.append(obj[search_key])
else:
for key, value in obj.items():
if (isinstance(value, list) or isinstance(value, dict)):
get_expressions(value, search_key, my_list)
return my_list
result = get_expressions(dashboard, "expr")
pretty_result = json.dumps(result, indent=2, sort_keys=True)
print(pretty_result)
Output
[
...
"node_memory_Shmem_bytes{instance=~\"$node:$port\",job=~\"$job\"}",
"node_memory_ShmemHugePages_bytes{instance=~\"$node:$port\",job=~\"$job\"}",
"node_memory_SUnreclaim_bytes{instance=~\"$node:$port\",job=~\"$job\"}",
"node_memory_SReclaimable_bytes{instance=~\"$node:$port\",job=~\"$job\"}",
"node_memory_VmallocChunk_bytes{instance=~\"$node:$port\",job=~\"$job\"}",
"node_memory_VmallocTotal_bytes{instance=~\"$node:$port\",job=~\"$job\"}",
...
]
obj is the dict type (elif), we check if search_key is in the dict, if it is, hooray, append to my_list (starts as an empty list) and return my_list (stop condition)search_key is not in the dict, we need to go through ALL the keys in the dict (just like we did in the list), if a value in the dict is type of list or dict, execute the function again (recursion)my_list which starts as an empty list by default, and finishes with the list of the values that we searched for (stop condition)I hope this was clear enough … :)